tag 2.1.1

git-svn-id: http://code.alibabatech.com/svn/dubbo/tags/2.1.1@1276 1a56cb94-b969-4eaa-88fa-be21384802f2
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..f44db6a
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,13 @@
+Copyright 1999-2012 Alibaba Group.

+ 

+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.
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9581678
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 1999-2012 Alibaba Group.
+
+   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.
diff --git a/README b/README
new file mode 100644
index 0000000..09b6735
--- /dev/null
+++ b/README
@@ -0,0 +1,124 @@
+For more, please refer to:
+
+    http://code.alibabatech.com/wiki/display/dubbo
+
+0. Install the subversion and maven command line:
+
+    yum install subversion
+    or: apt-get install subversion
+
+    cd ~
+    wget http://www.apache.org/dist//maven/binaries/apache-maven-2.2.1-bin.tar.gz
+    tar zxvf apache-maven-2.2.1-bin.tar.gz
+    vi .bash_profile
+       - edit: export PATH=$PATH:~/apache-maven-2.2.1/bin
+    source .bash_profile
+
+1. Checkout the dubbo source code:
+
+    cd ~
+    svn co http://code.alibabatech.com/svn/dubbo/trunk dubbo
+
+2. Import the dubbo source code to eclipse project:
+
+    cd ~/dubbo
+    mvn eclipse:eclipse
+    Eclipse -> Menu -> File -> Import -> Exsiting Projects to Workspace -> Browse -> Finish
+
+    Context Menu -> Run As -> Java Application:
+    dubbo-demo-provider/src/test/java/com.alibaba.dubbo.demo.provider.DemoProvider
+    dubbo-demo-consumer/src/test/java/com.alibaba.dubbo.demo.consumer.DemoConsumer
+    dubbo-monitor-simple/src/test/java/com.alibaba.dubbo.monitor.simple.SimpleMonitor
+    dubbo-registry-simple/src/test/java/com.alibaba.dubbo.registry.simple.SimpleRegistry
+
+    Edit Config:
+    dubbo-demo-provider/src/test/resources/dubbo.properties
+    dubbo-demo-consumer/src/test/resources/dubbo.properties
+    dubbo-monitor-simple/src/test/resources/dubbo.properties
+    dubbo-registry-simple/src/test/resources/dubbo.properties
+
+3. Build the dubbo binary package:
+
+    cd ~/dubbo
+    mvn clean install -Dmaven.test.skip
+    cd dubbo/target
+    ls
+
+4. Install the demo provider:
+
+    cd ~/dubbo/dubbo-demo-provider/target
+    tar zxvf dubbo-demo-provider-2.1.1-assembly.tar.gz
+    cd dubbo-demo-provider-2.1.1/bin
+    ./start.sh
+
+5. Install the demo consumer:
+
+    cd ~/dubbo/dubbo-demo-consumer/target
+    tar zxvf dubbo-demo-consumer-2.1.1-assembly.tar.gz
+    cd dubbo-demo-consumer-2.1.1/bin
+    ./start.sh
+    cd ../logs
+    tail -f stdout.log
+
+6. Install the simple monitor:
+
+    cd ~/dubbo/dubbo-simple-monitor/target
+    tar zxvf dubbo-simple-monitor-2.1.1-assembly.tar.gz
+    cd dubbo-simple-monitor-2.1.1/bin
+    ./start.sh
+    http://127.0.0.1:8080
+
+7. Install the simple registry:
+
+    cd ~/dubbo/dubbo-simple-registry/target
+    tar zxvf dubbo-simple-registry-2.1.1-assembly.tar.gz
+    cd dubbo-simple-registry-2.1.1/bin
+    ./start.sh
+
+    cd ~/dubbo/dubbo-demo-provider/conf
+    vi dubbo.properties
+       - edit: dubbo.registry.adddress=dubbo://127.0.0.1:9090
+    cd ../bin
+    ./restart.sh
+
+    cd ~/dubbo/dubbo-demo-consumer/conf
+    vi dubbo.properties
+       - edit: dubbo.registry.adddress=dubbo://127.0.0.1:9090
+    cd ../bin
+    ./restart.sh
+
+    cd ~/dubbo/dubbo-simple-monitor/conf
+    vi dubbo.properties
+       - edit: dubbo.registry.adddress=dubbo://127.0.0.1:9090
+    cd ../bin
+    ./restart.sh
+
+8. Install the zookeeper registry:
+
+    cd ~
+    wget http://www.apache.org/dist//zookeeper/zookeeper-3.3.3/zookeeper-3.3.3.tar.gz
+    tar zxvf zookeeper-3.3.3.tar.gz
+    cd zookeeper-3.3.3/conf
+    cp zoo_sample.cfg zoo.cfg
+    vi zoo.cfg
+       - edit: dataDir=/home/xxx/data
+    cd ../bin
+    ./zkServer.sh start
+
+    cd ~/dubbo/dubbo-demo-provider/conf
+    vi dubbo.properties
+       - edit: dubbo.registry.adddress=zookeeper://127.0.0.1:2181
+    cd ../bin
+    ./restart.sh
+
+    cd ~/dubbo/dubbo-demo-consumer/conf
+    vi dubbo.properties
+       - edit: dubbo.registry.adddress=zookeeper://127.0.0.1:2181
+    cd ../bin
+    ./restart.sh
+
+    cd ~/dubbo/dubbo-simple-monitor/conf
+    vi dubbo.properties
+       - edit: dubbo.registry.adddress=zookeeper://127.0.0.1:2181
+    cd ../bin
+    ./restart.sh
diff --git a/dubbo-cache/pom.xml b/dubbo-cache/pom.xml
new file mode 100644
index 0000000..6c643a1
--- /dev/null
+++ b/dubbo-cache/pom.xml
@@ -0,0 +1,43 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+	<modelVersion>4.0.0</modelVersion>

+	<parent>

+		<groupId>com.alibaba</groupId>

+		<artifactId>dubbo-parent</artifactId>

+		<version>2.1.1</version>

+	</parent>

+	<artifactId>dubbo-cache</artifactId>

+	<packaging>jar</packaging>

+	<name>Dubbo Cache Module</name>

+	<description>The cache module of dubbo project</description>

+	<properties>

+		<skip_maven_deploy>true</skip_maven_deploy>

+	</properties>

+	<dependencies>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>javax.cache</groupId>

+			<artifactId>cache-api</artifactId>

+			<scope>provided</scope>

+		</dependency>

+	</dependencies>

+</project>
\ No newline at end of file
diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/Cache.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/Cache.java
new file mode 100644
index 0000000..61a0399
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/Cache.java
@@ -0,0 +1,29 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache;

+

+/**

+ * Cache

+ * 

+ * @author william.liangf

+ */

+public interface Cache {

+

+    void put(Object key, Object value);

+

+    Object get(Object key);

+

+}

diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/CacheFactory.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/CacheFactory.java
new file mode 100644
index 0000000..4ad2e20
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/CacheFactory.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * CacheFactory

+ * 

+ * @author william.liangf

+ */

+@SPI("lru")

+public interface CacheFactory {

+

+    @Adaptive(value = "cache", method = "1")

+    Cache getCache(URL url, String name);

+

+}

diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java
new file mode 100644
index 0000000..9767b5c
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java
@@ -0,0 +1,66 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache.filter;

+

+import com.alibaba.dubbo.cache.Cache;

+import com.alibaba.dubbo.cache.CacheFactory;

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+

+/**

+ * CacheFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY)

+public class CacheFilter implements Filter {

+

+    private CacheFactory cacheFactory;

+

+    public void setCacheFactory(CacheFactory cacheFactory) {

+        this.cacheFactory = cacheFactory;

+    }

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.CACHE_KEY))) {

+            Cache cache = cacheFactory.getCache(invoker.getUrl(), invocation.getMethodName());

+            if (cache != null) {

+                String key = StringUtils.toArgumentString(invocation.getArguments());

+                if (cache != null && key != null) {

+                    Object value = cache.get(key);

+                    if (value != null) {

+                        return new RpcResult(value);

+                    }

+                    Result result = invoker.invoke(invocation);

+                    if (! result.hasException()) {

+                        cache.put(key, result.getValue());

+                    }

+                    return result;

+                }

+            }

+        }

+        return invoker.invoke(invocation);

+    }

+

+}

diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java
new file mode 100644
index 0000000..ef7a421
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache.support;

+

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+

+import com.alibaba.dubbo.cache.Cache;

+import com.alibaba.dubbo.cache.CacheFactory;

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * AbstractCacheFactory

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractCacheFactory implements CacheFactory {

+    

+    private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();

+

+    public Cache getCache(URL url, String name) {

+        String key = url.toFullString() + Constants.SEMICOLON_SEPARATOR + name;

+        Cache cache = caches.get(key);

+        if (cache == null) {

+            caches.put(key, createCache(url));

+            cache = caches.get(key);

+        }

+        return cache;

+    }

+

+    protected abstract Cache createCache(URL url);

+

+}

diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java
new file mode 100644
index 0000000..66fd5af
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java
@@ -0,0 +1,49 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache.support.jcache;

+

+import javax.cache.Cache;

+import javax.cache.CacheBuilder;

+import javax.cache.CacheManager;

+import javax.cache.Caching;

+

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * JCache

+ * 

+ * @author william.liangf

+ */

+public class JCache implements com.alibaba.dubbo.cache.Cache {

+

+    private final Cache<Object, Object> store;

+

+    public JCache(URL url) {

+        String type = url.getParameter("jcache");

+        CacheManager cacheManager = type == null || type.length() == 0 ? Caching.getCacheManager() : Caching.getCacheManager(type);

+        CacheBuilder<Object, Object> cacheBuilder = cacheManager.createCacheBuilder(url.getServiceKey());

+        this.store = cacheBuilder.build();

+    }

+

+    public void put(Object key, Object value) {

+        store.put(key, value);

+    }

+

+    public Object get(Object key) {

+        return store.get(key);

+    }

+

+}

diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java
new file mode 100644
index 0000000..c5e0cfd
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache.support.jcache;

+

+import com.alibaba.dubbo.cache.Cache;

+import com.alibaba.dubbo.cache.support.AbstractCacheFactory;

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * JCacheFactory

+ * 

+ * @author william.liangf

+ */

+public class JCacheFactory extends AbstractCacheFactory {

+

+    protected Cache createCache(URL url) {

+        return new JCache(url);

+    }

+

+}

diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java
new file mode 100644
index 0000000..33609dc
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java
@@ -0,0 +1,57 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache.support.lru;

+

+import java.util.LinkedHashMap;

+import java.util.Map;

+import java.util.Map.Entry;

+

+import com.alibaba.dubbo.cache.Cache;

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * LruCache

+ * 

+ * @author william.liangf

+ */

+public class LruCache implements Cache {

+    

+    private final Map<Object, Object> store;

+

+    public LruCache(URL url) {

+        final int max = url.getParameter("cache.size", 1000);

+        this.store = new LinkedHashMap<Object, Object>() {

+            private static final long serialVersionUID = -3834209229668463829L;

+            @Override

+            protected boolean removeEldestEntry(Entry<Object, Object> eldest) {

+                return size() > max;

+            }

+        };

+    }

+

+    public void put(Object key, Object value) {

+        synchronized (store) {

+            store.put(key, value);

+        }

+    }

+

+    public Object get(Object key) {

+        synchronized (store) {

+            return store.get(key);

+        }

+    }

+

+}

diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java
new file mode 100644
index 0000000..0e48999
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache.support.lru;

+

+import com.alibaba.dubbo.cache.Cache;

+import com.alibaba.dubbo.cache.support.AbstractCacheFactory;

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * LruCacheFactory

+ * 

+ * @author william.liangf

+ */

+public class LruCacheFactory extends AbstractCacheFactory {

+

+    protected Cache createCache(URL url) {

+        return new LruCache(url);

+    }

+

+}

diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java
new file mode 100644
index 0000000..9532696
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache.support.threadlocal;

+

+import java.util.HashMap;

+import java.util.Map;

+

+import com.alibaba.dubbo.cache.Cache;

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * ThreadLocalCache

+ * 

+ * @author william.liangf

+ */

+public class ThreadLocalCache implements Cache {

+

+    private final ThreadLocal<Map<Object, Object>> store;

+

+    public ThreadLocalCache(URL url) {

+        this.store = new ThreadLocal<Map<Object, Object>>() {

+            @Override

+            protected Map<Object, Object> initialValue() {

+                return new HashMap<Object, Object>();

+            }

+        };

+    }

+

+    public void put(Object key, Object value) {

+        store.get().put(key, value);

+    }

+

+    public Object get(Object key) {

+        return store.get().get(key);

+    }

+

+}

diff --git a/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java
new file mode 100644
index 0000000..ca7ace3
--- /dev/null
+++ b/dubbo-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.cache.support.threadlocal;

+

+import com.alibaba.dubbo.cache.Cache;

+import com.alibaba.dubbo.cache.support.AbstractCacheFactory;

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * ThreadLocalCacheFactory

+ * 

+ * @author william.liangf

+ */

+public class ThreadLocalCacheFactory extends AbstractCacheFactory {

+

+    protected Cache createCache(URL url) {

+        return new ThreadLocalCache(url);

+    }

+

+}

diff --git a/dubbo-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory b/dubbo-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory
new file mode 100644
index 0000000..6e3ddd0
--- /dev/null
+++ b/dubbo-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory
@@ -0,0 +1,3 @@
+threadlocal=com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory

+lru=com.alibaba.dubbo.cache.support.lru.LruCacheFactory

+jcache=com.alibaba.dubbo.cache.support.jcache.JCacheFactory
\ No newline at end of file
diff --git a/dubbo-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..ec25092
--- /dev/null
+++ b/dubbo-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+cache=com.alibaba.dubbo.cache.filter.CacheFilter
\ No newline at end of file
diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml
new file mode 100644
index 0000000..fb26c7c
--- /dev/null
+++ b/dubbo-cluster/pom.xml
@@ -0,0 +1,44 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+	<modelVersion>4.0.0</modelVersion>

+	<parent>

+		<groupId>com.alibaba</groupId>

+		<artifactId>dubbo-parent</artifactId>

+		<version>2.1.1</version>

+	</parent>

+	<artifactId>dubbo-cluster</artifactId>

+	<packaging>jar</packaging>

+	<name>Dubbo Cluster Module</name>

+	<description>The cluster module of dubbo project</description>

+	<properties>

+		<skip_maven_deploy>true</skip_maven_deploy>

+	</properties>

+	<dependencies>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.bsf</groupId>

+			<artifactId>bsf-api</artifactId>

+			<scope>provided</scope>

+			<optional>true</optional>

+		</dependency>

+	</dependencies>

+</project>
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
new file mode 100644
index 0000000..039c43b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster;

+

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.support.FailoverCluster;

+

+/**

+ * Cluster. (SPI, Singleton, ThreadSafe)

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Computer_cluster">Cluster</a>

+ * <a href="http://en.wikipedia.org/wiki/Fault-tolerant_system">Fault-Tolerant</a>

+ * 

+ * @author william.liangf

+ */

+@SPI(FailoverCluster.NAME)

+public interface Cluster {

+

+    /**

+     * Merge the directory invokers to a virtual invoker.

+     * 

+     * @param <T>

+     * @param directory

+     * @return cluster invoker

+     * @throws RpcException

+     */

+    @Adaptive

+    <T> Invoker<T> join(Directory<T> directory) throws RpcException;

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java
new file mode 100644
index 0000000..1729b5a
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java
@@ -0,0 +1,49 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.Node;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * Directory. (SPI, Prototype, ThreadSafe)

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Directory_service">Directory Service</a>

+ * 

+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)

+ * @author william.liangf

+ */

+public interface Directory<T> extends Node {

+    

+    /**

+     * get service type.

+     * 

+     * @return service type.

+     */

+    Class<T> getInterface();

+

+    /**

+     * list invokers.

+     * 

+     * @return invokers

+     */

+    List<Invoker<T>> list(Invocation invocation) throws RpcException;

+    

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java
new file mode 100644
index 0000000..c688c1c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster;
+
+import java.util.List;

+

+import com.alibaba.dubbo.common.extension.SPI;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;

+
+/**
+ * LoadBalance. (SPI, Singleton, ThreadSafe)
+ * 
+ * <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
+ * 
+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
+ * @author qian.lei
+ * @author william.liangf
+ */
+@SPI(RandomLoadBalance.NAME)
+public interface LoadBalance {
+
+	/**
+	 * select one invoker in list.
+	 * 
+	 * @param invokers invokers.
+	 * @param invocation invocation.
+	 * @return selected invoker.
+	 */
+	<T> Invoker<T> select(List<Invoker<T>> invokers, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Merger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Merger.java
new file mode 100644
index 0000000..2009f9e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Merger.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@SPI
+public interface Merger<T> {
+
+    T merge(T... items);
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
new file mode 100644
index 0000000..65b72ac
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
@@ -0,0 +1,45 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster;
+
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * Router. (SPI, Prototype, ThreadSafe)
+ * 

+ * <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>

+ * 

+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)

+ * @see com.alibaba.dubbo.rpc.cluster.Directory#list(Invocation)
+ * @author chao.liuc
+ */
+public interface Router {
+
+    /**
+     * route.
+     * 
+     * @param invokers
+     * @param invocation
+     * @return routed invokers
+     * @throws RpcException
+     */
+	<T> List<Invoker<T>> route(List<Invoker<T>> invokers, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java
new file mode 100644
index 0000000..b9b46b5
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java
@@ -0,0 +1,44 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+import com.alibaba.dubbo.rpc.Invocation;

+
+/**
+ * RouterFactory. (SPI, Singleton, ThreadSafe)
+ * 

+ * <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>

+ * 

+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)

+ * @see com.alibaba.dubbo.rpc.cluster.Directory#list(Invocation)
+ * @author chao.liuc
+ */

+@SPI
+public interface RouterFactory {
+    
+    /**
+     * Create router.
+     * 
+     * @param url
+     * @return router
+     */
+    @Adaptive("protocol")
+    Router getRouter(URL url);
+    
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/AbstractDirectory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/AbstractDirectory.java
new file mode 100644
index 0000000..7b4251b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/AbstractDirectory.java
@@ -0,0 +1,95 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.directory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+import com.alibaba.dubbo.rpc.cluster.router.MockInvokersSelector;

+
+/**
+ * 增加router的Directory

+ * 
+ * @author chao.liuc
+ */
+public abstract class AbstractDirectory<T> implements Directory<T> {
+    
+    private final URL url ;
+    protected volatile boolean destroyed = false;
+
+    private List<Router> routers = new ArrayList<Router>();
+    
+    public AbstractDirectory(URL url) {
+        this(url, null);
+    }
+    
+    public AbstractDirectory(URL url, List<Router> routers) {
+        if (url == null)
+            throw new IllegalArgumentException("url == null");
+        if (routers == null){
+            routers = new ArrayList<Router>();
+        }
+        
+        this.url = url;
+        String routerkey = url.getParameter(Constants.ROUTER_KEY);
+        if (routerkey != null && routerkey.length()>0 ){
+            RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
+            routers.add(routerFactory.getRouter(url));
+        }

+        setRouters(routers);
+    }
+    
+    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
+        if (destroyed){
+            throw new RpcException("Directory already destroyed .url: "+ getUrl());
+        }
+        List<Invoker<T>> invokers = doList(invocation);

+

+        for (Router router: routers){
+            invokers = router.route(invokers, invocation);
+        }

+        return invokers;
+    }
+    
+    public URL getUrl() {
+        return url;
+    }
+    
+    public List<Router> getRouters(){
+        return routers;
+    }
+    
+    protected abstract List<Invoker<T>> doList(Invocation invocation) throws RpcException ;
+    
+    protected void setRouters(final List<Router> r){

+    	routers = new ArrayList<Router>(r);

+        //mock invoker选择器开启

+        routers.add(new MockInvokersSelector());
+    }
+    
+    public void destroy(){
+        destroyed = true;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.java
new file mode 100644
index 0000000..c9ce86c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.java
@@ -0,0 +1,85 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.directory;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Router;

+

+/**

+ * StaticDirectory

+ * 

+ * @author william.liangf

+ */

+public class StaticDirectory<T> extends AbstractDirectory<T> {

+    

+    private final List<Invoker<T>> invokers;

+    

+    public StaticDirectory(List<Invoker<T>> invokers){

+        this(null, invokers, null);

+    }

+    

+    public StaticDirectory(List<Invoker<T>> invokers, List<Router> routers){

+        this(null, invokers, routers);

+    }

+    

+    public StaticDirectory(URL url, List<Invoker<T>> invokers) {

+        this(url, invokers, null);

+    }

+

+    public StaticDirectory(URL url, List<Invoker<T>> invokers, List<Router> routers) {

+        super(url == null && invokers != null && invokers.size() > 0 ? invokers.get(0).getUrl() : url, routers);

+        if (invokers == null || invokers.size() == 0)

+            throw new IllegalArgumentException("invokers == null");

+        this.invokers = invokers;

+    }

+

+    public Class<T> getInterface() {

+        return invokers.get(0).getInterface();

+    }

+

+    public boolean isAvailable() {

+        if (destroyed) return false;

+        for (Invoker<T> invoker : invokers) {

+            if (invoker.isAvailable()) {

+                return true;

+            }

+        }

+        return false;

+    }

+

+    public void destroy() {

+        if(destroyed) {

+            return;

+        }

+        super.destroy();

+        for (Invoker<T> invoker : invokers) {

+            invoker.destroy();

+        }

+        invokers.clear();

+    }

+    

+    @Override

+    protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {

+

+        return invokers;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java
new file mode 100644
index 0000000..a1d9b13
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.loadbalance;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+

+/**

+ * AbstractLoadBalance

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractLoadBalance implements LoadBalance {

+

+    public <T> Invoker<T> select(List<Invoker<T>> invokers, Invocation invocation) {

+        if (invokers == null || invokers.size() == 0)

+            return null;

+        if (invokers.size() == 1)

+            return invokers.get(0);

+        return doSelect(invokers, invocation);

+    }

+

+    protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation);

+

+    protected int getWeight(Invoker<?> invoker, Invocation invocation) {

+        return invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java
new file mode 100644
index 0000000..4f50797
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java
@@ -0,0 +1,149 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.loadbalance;

+

+import java.io.UnsupportedEncodingException;

+import java.security.MessageDigest;

+import java.security.NoSuchAlgorithmException;

+import java.util.List;

+import java.util.SortedMap;

+import java.util.TreeMap;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+

+/**

+ * ConsistentHashLoadBalance

+ * 

+ * @author william.liangf

+ */

+public class ConsistentHashLoadBalance extends AbstractLoadBalance {

+

+    private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();

+

+    @SuppressWarnings("unchecked")

+    @Override

+    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation) {

+        String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();

+        int identityHashCode = System.identityHashCode(invokers);

+        ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);

+        if (selector == null || selector.getIdentityHashCode() != identityHashCode) {

+            selectors.put(key, new ConsistentHashSelector<T>(invokers, invocation.getMethodName(), identityHashCode));

+            selector = (ConsistentHashSelector<T>) selectors.get(key);

+        }

+        return selector.select(invocation);

+    }

+

+    private static final class ConsistentHashSelector<T> {

+

+        private final TreeMap<Long, Invoker<T>> virtualInvokers;

+

+        private final int                       replicaNumber;

+        

+        private final int                       identityHashCode;

+        

+        private final int[]                     argumentIndex;

+

+        public ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {

+            this.virtualInvokers = new TreeMap<Long, Invoker<T>>();

+            this.identityHashCode = System.identityHashCode(invokers);

+            URL url = invokers.get(0).getUrl();

+            this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);

+            String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0"));

+            argumentIndex = new int[index.length];

+            for (int i = 0; i < index.length; i ++) {

+                argumentIndex[i] = Integer.parseInt(index[i]);

+            }

+            for (Invoker<T> invoker : invokers) {

+                for (int i = 0; i < replicaNumber / 4; i++) {

+                    byte[] digest = md5(invoker.getUrl().toFullString() + i);

+                    for (int h = 0; h < 4; h++) {

+                        long m = hash(digest, h);

+                        virtualInvokers.put(m, invoker);

+                    }

+                }

+            }

+        }

+

+        public int getIdentityHashCode() {

+            return identityHashCode;

+        }

+

+        public Invoker<T> select(Invocation invocation) {

+            String key = toKey(invocation.getArguments());

+            byte[] digest = md5(key);

+            Invoker<T> invoker = sekectForKey(hash(digest, 0));

+            return invoker;

+        }

+

+        private String toKey(Object[] args) {

+            StringBuilder buf = new StringBuilder();

+            for (int i : argumentIndex) {

+                if (i >= 0 && i < args.length) {

+                    buf.append(args[i]);

+                }

+            }

+            return buf.toString();

+        }

+

+        private Invoker<T> sekectForKey(long hash) {

+            Invoker<T> invoker;

+            Long key = hash;

+            if (!virtualInvokers.containsKey(key)) {

+                SortedMap<Long, Invoker<T>> tailMap = virtualInvokers.tailMap(key);

+                if (tailMap.isEmpty()) {

+                    key = virtualInvokers.firstKey();

+                } else {

+                    key = tailMap.firstKey();

+                }

+            }

+            invoker = virtualInvokers.get(key);

+            return invoker;

+        }

+

+        private long hash(byte[] digest, int number) {

+            return (((long) (digest[3 + number * 4] & 0xFF) << 24)

+                    | ((long) (digest[2 + number * 4] & 0xFF) << 16)

+                    | ((long) (digest[1 + number * 4] & 0xFF) << 8) 

+                    | (digest[0 + number * 4] & 0xFF)) 

+                    & 0xFFFFFFFFL;

+        }

+

+        private byte[] md5(String value) {

+            MessageDigest md5;

+            try {

+                md5 = MessageDigest.getInstance("MD5");

+            } catch (NoSuchAlgorithmException e) {

+                throw new IllegalStateException(e.getMessage(), e);

+            }

+            md5.reset();

+            byte[] bytes = null;

+            try {

+                bytes = value.getBytes("UTF-8");

+            } catch (UnsupportedEncodingException e) {

+                throw new IllegalStateException(e.getMessage(), e);

+            }

+            md5.update(bytes);

+            return md5.digest();

+        }

+

+    }

+

+}

diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java
new file mode 100644
index 0000000..0ceb409
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java
@@ -0,0 +1,85 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.loadbalance;

+

+import java.util.List;

+import java.util.Random;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcStatus;

+

+/**

+ * LeastActiveLoadBalance

+ * 

+ * @author william.liangf

+ */

+public class LeastActiveLoadBalance extends AbstractLoadBalance {

+

+    public static final String NAME = "leastactive";

+    

+    private final Random random = new Random();

+

+    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation) {

+        int length = invokers.size(); // 总个数

+        int leastActive = -1; // 最小的活跃数

+        int leastCount = 0; // 相同最小活跃数的个数

+        int[] leastIndexs = new int[length]; // 相同最小活跃数的下标

+        int totalWeight = 0; // 总权重

+        int firstWeight = 0; // 第一个权重,用于于计算是否相同

+        boolean sameWeight = true; // 是否所有权重相同

+        for (int i = 0; i < length; i++) {

+        	Invoker<T> invoker = invokers.get(i);

+            int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive(); // 活跃数

+            int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT); // 权重

+            if (leastActive == -1 || active < leastActive) { // 发现更小的活跃数,重新开始

+                leastActive = active; // 记录最小活跃数

+                leastCount = 1; // 重新统计相同最小活跃数的个数

+                leastIndexs[0] = i; // 重新记录最小活跃数下标

+                totalWeight = weight; // 重新累计总权重

+                firstWeight = weight; // 记录第一个权重

+                sameWeight = true; // 还原权重相同标识

+            } else if (active == leastActive) { // 累计相同最小的活跃数

+                leastIndexs[leastCount ++] = i; // 累计相同最小活跃数下标

+                totalWeight += weight; // 累计总权重

+                // 判断所有权重是否一样

+                if (sameWeight && i > 0 

+                        && weight != firstWeight) {

+                    sameWeight = false;

+                }

+            }

+        }

+        // assert(leastCount > 0)

+        if (leastCount == 1) {

+            // 如果只有一个最小则直接返回

+            return invokers.get(leastIndexs[0]);

+        }

+        if (! sameWeight && totalWeight > 0) {

+            // 如果权重不相同且权重大于0则按总权重数随机

+            int offsetWeight = random.nextInt(totalWeight);

+            // 并确定随机值落在哪个片断上

+            for (int i = 0; i < leastCount; i++) {

+                int leastIndex = leastIndexs[i];

+                offsetWeight -= getWeight(invokers.get(leastIndex), invocation);

+                if (offsetWeight <= 0)

+                    return invokers.get(leastIndex);

+            }

+        }

+        // 如果权重相同或权重为0则均等随机

+        return invokers.get(leastIndexs[random.nextInt(leastCount)]);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.java
new file mode 100644
index 0000000..a12c7eb
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.java
@@ -0,0 +1,54 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * LoadBalanceAdptive
+ * 
+ * @author ding.lid
+ * @author william.liangf
+ */
+@Adaptive
+public class LoadBalanceAdptive implements LoadBalance {

+
+    public <T> Invoker<T> select(List<Invoker<T>> invokers, Invocation invocation) throws RpcException {
+        if (invokers == null || invokers.size() == 0) {
+            return null;
+        }
+        URL url = invokers.get(0).getUrl();
+        String method = invocation.getMethodName();
+        String name;
+        if (method == null || method.length() == 0) {
+            name = url.getParameter(Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE);
+        } else {
+            name = url.getMethodParameter(method, Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE);
+        }
+        LoadBalance loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(name);
+        return loadbalance.select(invokers, invocation);
+    }

+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
new file mode 100644
index 0000000..6e903459
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
@@ -0,0 +1,63 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;

+import java.util.Random;

+

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+
+/**
+ * random load balance.
+ *
+ * @author qianlei
+ * @author william.liangf
+ */
+public class RandomLoadBalance extends AbstractLoadBalance {

+
+    public static final String NAME = "random";
+
+    private final Random random = new Random();
+
+    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation) {
+        int length = invokers.size(); // 总个数

+        int totalWeight = 0; // 总权重
+        boolean sameWeight = true; // 权重是否都一样
+        for (int i = 0; i < length; i++) {
+            int weight = getWeight(invokers.get(i), invocation);
+            totalWeight += weight; // 累计总权重
+            if (sameWeight && i > 0
+                    && weight != getWeight(invokers.get(i - 1), invocation)) {
+                sameWeight = false; // 计算所有权重是否一样
+            }
+        }
+        if (totalWeight > 0 && ! sameWeight) {
+            // 如果权重不相同且权重大于0则按总权重数随机
+            int offset = random.nextInt(totalWeight);
+            // 并确定随机值落在哪个片断上
+            for (int i = 0; i < length; i++) {
+                offset -= getWeight(invokers.get(i), invocation);
+                if (offset < 0) {
+                    return invokers.get(i);
+                }
+            }
+        }
+        // 如果权重相同或权重为0则均等随机
+        return invokers.get(random.nextInt(length));
+    }

+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java
new file mode 100644
index 0000000..e826f33
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java
@@ -0,0 +1,81 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.loadbalance;
+
+import java.util.ArrayList;

+import java.util.List;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+

+import com.alibaba.dubbo.common.utils.AtomicPositiveInteger;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+
+/**
+ * Round robin load balance.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class RoundRobinLoadBalance extends AbstractLoadBalance {

+
+    public static final String NAME = "roundrobin"; 
+    
+    private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
+

+    private final ConcurrentMap<String, AtomicPositiveInteger> weightSequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();

+
+    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation) {
+        String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
+        int length = invokers.size(); // 总个数

+        int maxWeight = 0; // 最大权重

+        int minWeight = Integer.MAX_VALUE; // 最小权重

+        for (int i = 0; i < length; i++) {

+            int weight = getWeight(invokers.get(i), invocation);

+            maxWeight = Math.max(maxWeight, weight); // 累计最大权重

+            minWeight = Math.min(minWeight, weight); // 累计最小权重

+        }

+        if (maxWeight > 0 && minWeight < maxWeight) { // 权重不一样

+            AtomicPositiveInteger weightSequence = weightSequences.get(key);

+            if (weightSequence == null) {

+                weightSequences.putIfAbsent(key, new AtomicPositiveInteger());

+                weightSequence = weightSequences.get(key);

+            }

+            int currentWeight = weightSequence.getAndIncrement() % maxWeight;

+            List<Invoker<T>> weightInvokers = new ArrayList<Invoker<T>>();

+            for (Invoker<T> invoker : invokers) { // 筛选权重大于当前权重基数的Invoker

+                if (getWeight(invoker, invocation) > currentWeight) {

+                    weightInvokers.add(invoker);

+                }

+            }

+            int weightLength = weightInvokers.size();

+            if (weightLength == 1) {

+                return weightInvokers.get(0);

+            } else if (weightLength > 1) {

+                invokers = weightInvokers;

+                length = invokers.size();

+            }

+        }
+        AtomicPositiveInteger sequence = sequences.get(key);

+        if (sequence == null) {

+            sequences.putIfAbsent(key, new AtomicPositiveInteger());

+            sequence = sequences.get(key);

+        }

+        // 取模轮循
+        return invokers.get(sequence.getAndIncrement() % length);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ArrayMerger.java
new file mode 100644
index 0000000..d88c290
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ArrayMerger.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+import java.lang.reflect.Array;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class ArrayMerger implements Merger<Object> {
+
+    public static final String      NAME     = "array";
+
+    public static final ArrayMerger INSTANCE = new ArrayMerger();
+
+    public Object merge(Object... others) {
+        if (others.length == 0) {
+            return null;
+        }
+        int totalLen = 0;
+        for(int i = 0; i < others.length; i++) {
+            Object item = others[i];
+            if (item != null && item.getClass().isArray()) {
+                totalLen += Array.getLength(item);
+            } else {
+                throw new IllegalArgumentException(
+                        new StringBuilder(32).append(i + 1)
+                                .append("th argument is not an array").toString());
+            }
+        }
+
+        if (totalLen == 0) { return null;}
+
+        Class<?> type = others[0].getClass().getComponentType();
+
+        Object result = Array.newInstance(type, totalLen);
+        if (boolean.class == type) {
+            mergeArray(result, BOOLEAN_ARRAY_FILLER, others);
+        } else if (char.class == type) {
+            mergeArray(result, CHAR_ARRAY_FILLER, others);
+        } else if (byte.class == type) {
+            mergeArray(result, BYTE_ARRAY_FILLER, others);
+        } else if (short.class == type) {
+            mergeArray(result, SHORT_ARRAY_FILLER, others);
+        } else if (int.class == type) {
+            mergeArray(result, INT_ARRAY_FILLER, others);
+        } else if (long.class == type) {
+            mergeArray(result, LONG_ARRAY_FILLER, others);
+        } else if (float.class == type) {
+            mergeArray(result, FLOAT_ARRAY_FILLER, others);
+        } else if (double.class == type) {
+            mergeArray(result, DOUBLE_ARRAY_FILLER, others);
+        } else {
+            mergeArray(result, OBJECT_ARRAY_FILLER, others);
+        }
+        return result;
+    }
+    
+    static interface ArrayFiller {
+        void fill(Object in, int index, Object out);
+    }
+
+    private static final ArrayFiller BOOLEAN_ARRAY_FILLER = new ArrayFiller() {
+
+        public void fill(Object in, int index, Object out) {
+            int len = Array.getLength( out );
+            for( int i = 0; i < len; i++) {
+                Array.setBoolean(in, index++, Array.getBoolean(out, i));
+            }
+        }
+    };
+
+    private static final ArrayFiller CHAR_ARRAY_FILLER = new ArrayFiller() {
+
+        public void fill(Object in, int index, Object out) {
+            int len = Array.getLength(out);
+            for (int i = 0; i < len; i++) {
+                Array.setChar(in, index++, Array.getChar(out, i));
+            }
+        }
+    };
+
+    private static final ArrayFiller BYTE_ARRAY_FILLER = new ArrayFiller() {
+
+        public void fill(Object in, int index, Object out) {
+            int len = Array.getLength(out);
+            for (int i = 0; i < len; i++) {
+                Array.setByte(in, index++, Array.getByte(out, i));
+            }
+        }
+    };
+
+    private static final ArrayFiller SHORT_ARRAY_FILLER = new ArrayFiller() {
+
+        public void fill(Object in, int index, Object out) {
+            int len = Array.getLength(out);
+            for (int i = 0; i < len; i++) {
+                Array.setShort(in, index++, Array.getShort(out, i));
+            }
+        }
+    };
+
+    private static final ArrayFiller INT_ARRAY_FILLER = new ArrayFiller() {
+
+        public void fill(Object in, int index, Object out) {
+            int len = Array.getLength(out);
+            for (int i = 0; i < len; i++) {
+                Array.setInt(in, index++, Array.getInt(out, i));
+            }
+        }
+    };
+
+    private static final ArrayFiller LONG_ARRAY_FILLER = new ArrayFiller() {
+
+        public void fill(Object in, int index, Object out) {
+            int len = Array.getLength(out);
+            for (int i = 0; i < len; i++) {
+                Array.setLong(in, index++, Array.getLong(out, i));
+            }
+        }
+    };
+
+    private static final ArrayFiller FLOAT_ARRAY_FILLER = new ArrayFiller() {
+
+        public void fill(Object in, int index, Object out) {
+            int len = Array.getLength(out);
+            for (int i = 0; i < len; i++) {
+                Array.setFloat(in, index++, Array.getFloat(out, i));
+            }
+        }
+    };
+
+    private static final ArrayFiller DOUBLE_ARRAY_FILLER = new ArrayFiller() {
+
+        public void fill(Object in, int index, Object out) {
+            int len = Array.getLength(out);
+            for (int i = 0; i < len; i++) {
+                Array.setDouble(in, index++, Array.getDouble(out, i));
+            }
+        }
+    };
+
+    private static final ArrayFiller OBJECT_ARRAY_FILLER = new ArrayFiller() {
+
+        public void fill(Object in, int index, Object out) {
+            int len = Array.getLength(out);
+            for (int i = 0; i < len; i++) {
+                Array.set(in, index++, Array.get(out, i));
+            }
+        }
+    };
+    
+    static void mergeArray(Object result, ArrayFiller filler, Object ... others) {
+        int index = 0;
+        for(int i = 0; i < others.length; i++) {
+            Object item = others[i];
+            if (item != null) {
+                filler.fill(result, index, item);
+                index += Array.getLength(item);
+            }
+        }
+    }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ListMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ListMerger.java
new file mode 100644
index 0000000..bc46ebf
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ListMerger.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *    
+ *   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.dubbo.rpc.cluster.merger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class ListMerger implements Merger<List<?>> {
+
+    public static final String     NAME     = "list";
+
+    public static final ListMerger INSTANCE = new ListMerger();
+
+    public List<Object> merge(List<?>... items) {
+        List<Object> result = new ArrayList<Object>();
+        for (List<?> item : items) {
+            if (item != null) {
+                result.addAll(item);
+            }
+        }
+        return result;
+    }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/MapMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/MapMerger.java
new file mode 100644
index 0000000..3c813f8
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/MapMerger.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.cluster.merger;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MapMerger implements Merger<Map<?, ?>> {
+
+    public static final String    NAME     = "map";
+
+    public static final MapMerger INSTANCE = new MapMerger();
+
+    public Map<?, ?> merge(Map<?, ?>... items) {
+        if (items.length == 0) {
+            return null;
+        }
+        Map<Object, Object> result = new HashMap<Object, Object>();
+        for (Map<?, ?> item : items) {
+            if (item != null) {
+                result.putAll(item);
+            }
+        }
+        return result;
+    }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/SetMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/SetMerger.java
new file mode 100644
index 0000000..de6464a
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/SetMerger.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *    
+ *   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.dubbo.rpc.cluster.merger;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class SetMerger implements Merger<Set<?>> {
+
+    public static final String    NAME     = "set";
+
+    public static final SetMerger INSTANCE = new SetMerger();
+
+    public Set<Object> merge(Set<?>... items) {
+
+        Set<Object> result = new HashSet<Object>();
+
+        for (Set<?> item : items) {
+            if (item != null) {
+                result.addAll(item);
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.java
new file mode 100644
index 0000000..cc9ee979
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.router;
+
+import java.io.File;

+import java.io.FileReader;

+import java.io.IOException;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.IOUtils;

+import com.alibaba.dubbo.rpc.cluster.Router;

+import com.alibaba.dubbo.rpc.cluster.RouterFactory;

+
+public class FileRouterFactory implements RouterFactory {
+    
+    public static final String NAME = "file";
+    
+    private RouterFactory routerFactory;
+    
+    public void setRouterFactory(RouterFactory routerFactory) {
+        this.routerFactory = routerFactory;
+    }
+    
+    public Router getRouter(URL url) {
+        try {
+            // File URL 转换成 其它Route URL,然后Load
+            // file:///d:/path/to/route.js?router=script ==> script:///d:/path/to/route.js?type=js&rule=<file-content>
+            String protocol = url.getParameter(Constants.ROUTER_KEY, ScriptRouterFactory.NAME); // 将原类型转为协议
+            String type = null; // 使用文件后缀做为类型
+            String path = url.getPath();
+            if (path != null) {
+                int i = path.lastIndexOf('.');
+                if (i > 0) {
+                    type = path.substring(i + 1);
+                }
+            }
+            String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath())));
+            URL script = url.setProtocol(protocol).addParameter(Constants.TYPE_KEY, type).addParameterAndEncoded(Constants.RULE_KEY, rule);
+            
+            return routerFactory.getRouter(script);
+        } catch (IOException e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.java
new file mode 100644
index 0000000..0f7506c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.java
@@ -0,0 +1,86 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.router;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Router;

+

+/**

+ * mock invoker选择器

+ * @author chao.liuc

+ *

+ */

+public class MockInvokersSelector implements Router {

+

+	public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,

+			final Invocation invocation) throws RpcException {

+		if (invocation.getAttachments() == null) {

+			return getNormalInvokers(invokers);

+		} else {

+			String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);

+			if (value == null) 

+				return getNormalInvokers(invokers);

+			else if (Boolean.TRUE.toString().equalsIgnoreCase(value)){

+				return getMockedInvokers(invokers);

+			} 

+		}

+		return invokers;

+	}

+	

+	private <T> List<Invoker<T>> getMockedInvokers(final List<Invoker<T>> invokers) {

+		if (! hasMockProviders(invokers)){

+			return null;

+		}

+		List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(1);

+		for (Invoker<T> invoker : invokers){

+			if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)){

+				sInvokers.add(invoker);

+			}

+		}

+		return sInvokers;

+	}

+	

+	private <T> List<Invoker<T>> getNormalInvokers(final List<Invoker<T>> invokers){

+		if (! hasMockProviders(invokers)){

+			return invokers;

+		} else {

+			List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(invokers.size());

+			for (Invoker<T> invoker : invokers){

+				if (! invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)){

+					sInvokers.add(invoker);

+				}

+			}

+			return sInvokers;

+		}

+	}

+	

+	private <T> boolean hasMockProviders(final List<Invoker<T>> invokers){

+		boolean hasMockProvider = false;

+		for (Invoker<T> invoker : invokers){

+			if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)){

+				hasMockProvider = true;

+				break;

+			}

+		}

+		return hasMockProvider;

+	}

+}

diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java
new file mode 100644
index 0000000..d1f456e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java
@@ -0,0 +1,106 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.router;

+

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import javax.script.Bindings;

+import javax.script.Compilable;

+import javax.script.CompiledScript;

+import javax.script.ScriptEngine;

+import javax.script.ScriptEngineManager;

+import javax.script.ScriptException;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Router;

+

+/**

+ * ScriptRouter

+ * 

+ * @author william.liangf

+ */

+public class ScriptRouter implements Router {

+

+    private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class);

+    

+    private static final Map<String, ScriptEngine> engines = new ConcurrentHashMap<String, ScriptEngine>();

+    

+    private final ScriptEngine engine;

+    

+    private final String rule;

+    

+    public ScriptRouter(URL url) {

+        String type = url.getParameter(Constants.TYPE_KEY);

+        String rule = url.getParameterAndDecoded(Constants.RULE_KEY);

+        if (type == null || type.length() == 0){

+            type = Constants.DEFAULT_SCRIPT_TYPE_KEY;

+        }

+        if (rule == null || rule.length() == 0){

+            throw new IllegalStateException(new IllegalStateException("route rule can not be empty. rule:" + rule));

+        }

+        ScriptEngine engine = engines.get(type);

+        if (engine == null){

+            engine = new ScriptEngineManager().getEngineByName(type);

+            if (engine == null) {

+                throw new IllegalStateException(new IllegalStateException("Unsupported route rule type: " + type + ", rule: " + rule));

+            }

+            engines.put(type, engine);

+        }

+        this.engine = engine;

+        this.rule = rule;

+    }

+

+    @SuppressWarnings("unchecked")

+    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, Invocation invocation) throws RpcException {

+        try {

+            List<Invoker<T>> invokersCopy = new ArrayList<Invoker<T>>(invokers);

+            Compilable compilable = (Compilable) engine;

+            Bindings bindings = engine.createBindings();

+            bindings.put("invokers", invokersCopy);

+            bindings.put("invocation", invocation);

+            bindings.put("context", RpcContext.getContext());

+            CompiledScript function = compilable.compile(rule);

+            Object obj = function.eval(bindings);

+            if (obj instanceof Invoker[]) {

+                invokersCopy = Arrays.asList((Invoker<T>[]) obj);

+            } else if (obj instanceof Object[]) {

+                invokersCopy = new ArrayList<Invoker<T>>();

+                for (Object inv : (Object[]) obj) {

+                    invokersCopy.add((Invoker<T>)inv);

+                }

+            } else {

+                invokersCopy = (List<Invoker<T>>) obj;

+            }

+            return invokersCopy;

+        } catch (ScriptException e) {

+            //fail then ignore rule .invokers.

+            logger.error("route error , rule has been ignored .rule :"+ rule + ",invocation:" + invocation + ",url :"+(RpcContext.getContext().getInvoker() == null ? "" : RpcContext.getContext().getInvoker().getUrl()), e);

+            return invokers;

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.java
new file mode 100644
index 0000000..6f4b70d
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.java
@@ -0,0 +1,44 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.router;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.cluster.Router;

+import com.alibaba.dubbo.rpc.cluster.RouterFactory;

+

+/**

+ * ScriptRouterFactory

+ * 

+ * Script Router Factory用到的URL形如:

+ * <ol>

+ * <li> script://registyAddress?type=js&rule=xxxx
+ * <li> script:///path/to/routerfile.js?type=js&rule=xxxx

+ * <li> script://D:\path\to\routerfile.js?type=js&rule=xxxx

+ * <li> script://C:/path/to/routerfile.js?type=js&rule=xxxx

+ * </ol>

+ * URL的Host一段包含的是Script Router内容的来源,Registry、File etc

+ * 

+ * @author william.liangf

+ */

+public class ScriptRouterFactory implements RouterFactory {

+    

+    public static final String NAME = "script";

+

+    public Router getRouter(URL url) {

+        return new ScriptRouter(url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
new file mode 100644
index 0000000..373db43
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
@@ -0,0 +1,258 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+

+/**

+ * AbstractClusterInvoker

+ * 

+ * @author william.liangf

+ * @author chao.liuc

+ */

+public abstract class AbstractClusterInvoker<T> implements Invoker<T> {

+

+    private static final Logger                logger                            = LoggerFactory

+                                                                                         .getLogger(AbstractClusterInvoker.class);

+    protected final Directory<T>               directory;

+

+    protected final boolean                    availablecheck;

+    

+    private volatile boolean                   destroyed = false;

+

+    private volatile Invoker<T>                stickyInvoker                     = null;

+

+    public AbstractClusterInvoker(Directory<T> directory) {

+        this(directory, directory.getUrl());

+    }

+    

+    public AbstractClusterInvoker(Directory<T> directory, URL url) {

+        if (directory == null)

+            throw new IllegalArgumentException("service directory == null");

+        

+        this.directory = directory ;

+        //sticky 需要检测 avaliablecheck 

+        this.availablecheck = url.getParameter(Constants.CLUSTER_AVAILABLE_CHECK_KEY, Constants.DEFAULT_CLUSTER_AVAILABLE_CHECK) ;

+    }

+

+    public Class<T> getInterface() {

+        return directory.getInterface();

+    }

+

+    public URL getUrl() {

+        return directory.getUrl();

+    }

+

+    public boolean isAvailable() {

+        Invoker<T> invoker = stickyInvoker;

+        if (invoker != null) {

+            return invoker.isAvailable();

+        }

+        return directory.isAvailable();

+    }

+

+    public void destroy() {

+        directory.destroy();

+        destroyed = true;

+    }

+

+    /**

+     * 使用loadbalance选择invoker.</br>

+     * a)先lb选择,如果在selected列表中 或者 不可用且做检验时,进入下一步(重选),否则直接返回</br>

+     * b)重选验证规则:selected > available .保证重选出的结果尽量不在select中,并且是可用的 

+     * 

+     * @param availablecheck 如果设置true,在选择的时候先选invoker.available == true

+     * @param selected 已选过的invoker.注意:输入保证不重复

+     * 

+     */

+    protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {

+        if (invokers == null || invokers.size() == 0)

+            return null;

+        String methodName = invocation == null ? "" : invocation.getMethodName();

+        

+        boolean sticky = invokers.get(0).getUrl().getMethodParameter(methodName,Constants.CLUSTER_STICKY_KEY, Constants.DEFAULT_CLUSTER_STICKY) ;

+        {

+            //ignore overloaded method

+            if ( stickyInvoker != null && !invokers.contains(stickyInvoker) ){

+                stickyInvoker = null;

+            }

+            //ignore cucurrent problem

+            if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))){

+                if (availablecheck && stickyInvoker.isAvailable()){

+                    return stickyInvoker;

+                }

+            }

+        }

+        Invoker<T> invoker = doselect(loadbalance, invocation, invokers, selected);

+        

+        if (sticky){

+            stickyInvoker = invoker;

+        }

+        return invoker;

+    }

+    

+    private Invoker<T> doselect(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {

+        if (invokers == null || invokers.size() == 0)

+            return null;

+        if (invokers.size() == 1)

+            return invokers.get(0);

+        // 如果只有两个invoker,退化成轮循

+        if (invokers.size() == 2 && selected != null && selected.size() > 0) {

+            return selected.get(0) == invokers.get(0) ? invokers.get(1) : invokers.get(0);

+        }

+        Invoker<T> invoker = loadbalance.select(invokers, invocation);

+        

+        //如果 selected中包含(优先判断) 或者 不可用&&availablecheck=true 则重试.

+        if( (selected != null && selected.contains(invoker))

+                ||(!invoker.isAvailable() && getUrl()!=null && availablecheck)){

+            try{

+                Invoker<T> rinvoker = reselect(loadbalance, invocation, invokers, selected, availablecheck);

+                if(rinvoker != null){

+                    invoker =  rinvoker;

+                }else{

+                    //看下第一次选的位置,如果不是最后,选+1位置.

+                    int index = invokers.indexOf(invoker);

+                    try{

+                        //最后在避免碰撞

+                        invoker = index <invokers.size()-1?invokers.get(index+1) :invoker;

+                    }catch (Exception e) {

+                        logger.warn(e.getMessage()+" may because invokers list dynamic change, ignore.",e);

+                    }

+                }

+            }catch (Throwable t){

+                logger.error("clustor relselect fail reason is :"+t.getMessage() +" if can not slove ,you can set cluster.availablecheck=false in url",t);

+            }

+        }

+        return invoker;

+    } 

+    

+    /**

+     * 重选,先从非selected的列表中选择,没有在从selected列表中选择.

+     * @param loadbalance

+     * @param invocation

+     * @param invokers

+     * @param selected

+     * @return

+     * @throws RpcException

+     */

+    private Invoker<T> reselect(LoadBalance loadbalance,Invocation invocation,

+                                List<Invoker<T>> invokers, List<Invoker<T>> selected ,boolean availablecheck)

+            throws RpcException {

+        

+        //预先分配一个,这个列表是一定会用到的.

+        List<Invoker<T>> reselectInvokers = new ArrayList<Invoker<T>>(invokers.size()>1?(invokers.size()-1):invokers.size());

+        

+        //先从非select中选

+        if( availablecheck ){ //选isAvailable 的非select

+            for(Invoker<T> invoker : invokers){

+                if(invoker.isAvailable()){

+                    if(selected ==null || !selected.contains(invoker)){

+                        reselectInvokers.add(invoker);

+                    }

+                }

+            }

+            if(reselectInvokers.size()>0){

+                return  loadbalance.select(reselectInvokers, invocation);

+            }

+        }else{ //选全部非select

+            for(Invoker<T> invoker : invokers){

+                if(selected ==null || !selected.contains(invoker)){

+                    reselectInvokers.add(invoker);

+                }

+            }

+            if(reselectInvokers.size()>0){

+                return  loadbalance.select(reselectInvokers, invocation);

+            }

+        }

+        //最后从select中选可用的. 

+        {

+            if(selected != null){

+                for(Invoker<T> invoker : selected){

+                    if((invoker.isAvailable()) //优先选available 

+                            && !reselectInvokers.contains(invoker)){

+                        reselectInvokers.add(invoker);

+                    }

+                }

+            }

+            if(reselectInvokers.size()>0){

+                return  loadbalance.select(reselectInvokers, invocation);

+            }

+        }

+        return null;

+    }

+    

+    public Result invoke(final Invocation invocation) throws RpcException {

+

+        checkWheatherDestoried();

+

+        LoadBalance loadbalance;

+        

+        List<Invoker<T>> invokers = directory.list(invocation);

+        if (invokers != null && invokers.size() > 0) {

+            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()

+                    .getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));

+        } else {

+            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);

+        }

+        

+        return doInvoke(invocation, invokers, loadbalance);

+    }

+

+    protected void checkWheatherDestoried() {

+

+        if(destroyed){

+            throw new RpcException("Rpc invoker for " + getInterface() + " on consumer " + NetUtils.getLocalHost()

+                    + " use dubbo version " + Version.getVersion()

+                    + " is not destroyed! Can not invoke any more.");

+        }

+    }

+

+    @Override

+    public String toString() {

+        return getInterface() + " -> " + getUrl().toString();

+    }

+    

+    protected void checkInvokers(List<Invoker<T>> invokers, Invocation invocation) {

+        if (invokers == null || invokers.size() == 0) {

+            throw new RpcException("Failed to invoke the method "

+                    + invocation.getMethodName() + " in the service " + getInterface().getName() 

+                    + ". No provider available for the service " + directory.getUrl().getServiceKey()

+                    + " from registry " + directory.getUrl().getAddress() 

+                    + " on the consumer " + NetUtils.getLocalHost()

+                    + " using the dubbo version " + Version.getVersion()

+                    + ". Please check if the providers have been started and registered.");

+        }

+    }

+

+    protected abstract Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,

+                                       LoadBalance loadbalance) throws RpcException;

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java
new file mode 100644
index 0000000..7b0de21
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java
@@ -0,0 +1,52 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import java.util.List;

+

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+

+/**

+ * AvailableCluster

+ * 

+ * @author william.liangf

+ */

+public class AvailableCluster implements Cluster {

+    

+    public static final String NAME = "available";

+

+    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

+        

+        return new AbstractClusterInvoker<T>(directory) {

+            public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {

+                for (Invoker<T> invoker : invokers) {

+                    if (invoker.isAvailable()) {

+                        return invoker.invoke(invocation);

+                    }

+                }

+                throw new RpcException("No provider available in " + invokers);

+            }

+        };

+        

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
new file mode 100644
index 0000000..98cb106
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AvailableCluster
+ * 
+ * @author william.liangf
+ */
+public class AvailableClusterInvoker<T> extends AbstractClusterInvoker<T> {

+
+    public AvailableClusterInvoker(Directory<T> directory) {
+        super(directory);
+    }
+
+    public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+        for (Invoker<T> invoker : invokers) {
+            if (invoker.isAvailable()) {
+                return invoker.invoke(invocation);
+            }
+        }
+        throw new RpcException("No provider available in " + invokers);
+    }

+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastCluster.java
new file mode 100644
index 0000000..907ec78
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastCluster.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+

+/**

+ * BroadcastCluster

+ * 

+ * @author william.liangf

+ */

+public class BroadcastCluster implements Cluster {

+

+    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

+        return new BroadcastClusterInvoker<T>(directory);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
new file mode 100644
index 0000000..e5d7412
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
@@ -0,0 +1,66 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+

+/**

+ * BroadcastClusterInvoker

+ * 

+ * @author william.liangf

+ */

+public class BroadcastClusterInvoker<T> extends AbstractClusterInvoker<T> {

+    

+    private static final Logger logger = LoggerFactory.getLogger(BroadcastClusterInvoker.class);

+

+    public BroadcastClusterInvoker(Directory<T> directory) {

+        super(directory);

+    }

+

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {

+        checkInvokers(invokers, invocation);

+        RpcContext.getContext().setInvokers((List)invokers);

+        RpcException exception = null;

+        Result result = null;

+        for (Invoker<T> invoker: invokers) {

+            try {

+                result = invoker.invoke(invocation);

+            } catch (RpcException e) {

+                exception = e;

+                logger.warn(e.getMessage(), e);

+            } catch (Throwable e) {

+                exception = new RpcException(e.getMessage(), e);

+                logger.warn(e.getMessage(), e);

+            }

+        }

+        if (exception != null) {

+            throw exception;

+        }

+        return result;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java
new file mode 100644
index 0000000..feadc3e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java
@@ -0,0 +1,86 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import java.util.HashMap;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * ClusterUtils

+ * 

+ * @author william.liangf

+ */

+public class ClusterUtils {

+    

+    public static URL mergeUrl(URL remoteUrl, Map<String, String> localMap) {

+        Map<String, String> map = new HashMap<String, String>();

+        Map<String, String> remoteMap = remoteUrl.getParameters();

+        

+        

+        if (remoteMap != null && remoteMap.size() > 0) {

+            map.putAll(remoteMap);

+            

+            //线程池配置不使用提供者的

+            map.remove(Constants.THREAD_NAME_KEY);

+            map.remove(Constants.THREADS_KEY);

+            map.remove(Constants.QUEUES_KEY);

+            map.remove(Constants.THREAD_ALIVE_KEY);

+        }

+        

+        if (localMap != null && localMap.size() > 0) {

+            map.putAll(localMap);

+        }

+        if (remoteMap != null && remoteMap.size() > 0) { 

+            // 版本号使用提供者的

+            String dubbo = remoteMap.get(Constants.DUBBO_VERSION_KEY);

+            if (dubbo != null && dubbo.length() > 0) {

+                map.put(Constants.DUBBO_VERSION_KEY, dubbo);

+            }

+            String version = remoteMap.get(Constants.VERSION_KEY);

+            if (version != null && version.length() > 0) {

+                map.put(Constants.VERSION_KEY, version);

+            }

+            String group = remoteMap.get(Constants.GROUP_KEY);

+            if (group != null && group.length() > 0) {

+                map.put(Constants.GROUP_KEY, group);

+            }

+            String methods = remoteMap.get(Constants.METHODS_KEY);

+            if (methods != null && methods.length() > 0) {

+                map.put(Constants.METHODS_KEY, methods);

+            }

+            // 合并filter和listener

+            String remoteFilter = remoteMap.get(Constants.REFERENCE_FILTER_KEY);

+            String localFilter = localMap.get(Constants.REFERENCE_FILTER_KEY);

+            if (remoteFilter != null && remoteFilter.length() > 0

+                    && localFilter != null && localFilter.length() > 0) {

+                localMap.put(Constants.REFERENCE_FILTER_KEY, remoteFilter + "," + localFilter);

+            }

+            String remoteListener = remoteMap.get(Constants.INVOKER_LISTENER_KEY);

+            String localListener = localMap.get(Constants.INVOKER_LISTENER_KEY);

+            if (remoteListener != null && remoteListener.length() > 0

+                    && localListener != null && localListener.length() > 0) {

+                localMap.put(Constants.INVOKER_LISTENER_KEY, remoteListener + "," + localListener);

+            }

+        }

+        return remoteUrl.addParameters(map);

+    }

+

+    private ClusterUtils() {}

+    

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.java
new file mode 100644
index 0000000..ac28746
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+

+/**

+ * 失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作。

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Failback">Failback</a>

+ * 

+ * @author william.liangf

+ */

+public class FailbackCluster implements Cluster {

+

+    public final static String NAME = "failback";    

+

+    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

+        return new FailbackClusterInvoker<T>(directory);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
new file mode 100644
index 0000000..57d68e6
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
@@ -0,0 +1,111 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+

+/**

+ * 失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作。

+ * 

+ * @author tony.chenl

+ */

+public class FailbackClusterInvoker<T> extends AbstractClusterInvoker<T> {

+

+    private static final Logger logger = LoggerFactory.getLogger(FailbackClusterInvoker.class);

+

+    private static final long RETRY_FAILED_PERIOD = 5 * 1000;

+

+    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2, new NamedThreadFactory("failback-cluster-timer", true));

+

+    private volatile ScheduledFuture<?> retryFuture;

+

+    private final ConcurrentMap<Invocation, AbstractClusterInvoker<?>> failed = new ConcurrentHashMap<Invocation, AbstractClusterInvoker<?>>();

+

+    public FailbackClusterInvoker(Directory<T> directory){

+        super(directory);

+    }

+

+    private void addFailed(Invocation invocation, AbstractClusterInvoker<?> router) {

+        if (retryFuture == null) {

+            synchronized (this) {

+                if (retryFuture == null) {

+                    retryFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {

+

+                        public void run() {

+                            // 收集统计信息

+                            try {

+                                retryFailed();

+                            } catch (Throwable t) { // 防御性容错

+                                logger.error("Unexpected error occur at collect statistic", t);

+                            }

+                        }

+                    }, RETRY_FAILED_PERIOD, RETRY_FAILED_PERIOD, TimeUnit.MILLISECONDS);

+                }

+            }

+        }

+        failed.put(invocation, router);

+    }

+

+    void retryFailed() {

+        if (failed.size() == 0) {

+            return;

+        }

+        for (Map.Entry<Invocation, AbstractClusterInvoker<?>> entry : new HashMap<Invocation, AbstractClusterInvoker<?>>(

+                                                                                                                         failed).entrySet()) {

+            Invocation invocation = entry.getKey();

+            Invoker<?> invoker = entry.getValue();

+            try {

+                invoker.invoke(invocation);

+                failed.remove(invocation);

+            } catch (Throwable e) {

+                logger.error("Failed retry to invoke " + invocation + ", waiting again.", e);

+            }

+        }

+    }

+

+    protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {

+        try {

+            checkInvokers(invokers, invocation);

+            Invoker<T> invoker = select(loadbalance, invocation, invokers, null);

+            return invoker.invoke(invocation);

+        } catch (Throwable e) {

+            logger.error("Failback to invoke " + invocation + ", wait for retry in background. Ignored exception: "

+                                 + e.getMessage() + ", ", e);

+            addFailed(invocation, this);

+            return new RpcResult(); // ignore

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java
new file mode 100644
index 0000000..4f39e25
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+

+/**

+ * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。

+ *  

+ * <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a>

+ * 

+ * @author william.liangf

+ */

+public class FailfastCluster implements Cluster {

+

+    public final static String NAME = "failfast";

+

+    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

+        return new FailfastClusterInvoker<T>(directory);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
new file mode 100644
index 0000000..d5a2f59
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
@@ -0,0 +1,55 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;
+
+import java.util.List;

+

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+
+/**
+ * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。

+ * 
+ * <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a>
+ * 
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class FailfastClusterInvoker<T> extends AbstractClusterInvoker<T>{

+
+    public FailfastClusterInvoker(Directory<T> directory) {
+        super(directory);
+    }
+    
+    public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+        checkInvokers(invokers, invocation);

+        Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+        try {
+            return invoker.invoke(invocation);
+        } catch (Throwable e) {

+            if (e instanceof RpcException && ((RpcException)e).isBiz()) { // biz exception.

+                throw (RpcException) e;

+            }
+            throw new RpcException(e instanceof RpcException ? ((RpcException)e).getCode() : 0, "Failfast invoke providers " + invoker.getUrl() + " " + loadbalance.getClass().getSimpleName() + " select from all providers " + invokers + " for service " + getInterface().getName() + " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e.getCause() != null ? e.getCause() : e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.java
new file mode 100644
index 0000000..773b292
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+

+/**

+ * 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。 

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Failover">Failover</a>

+ * 

+ * @author william.liangf

+ */

+public class FailoverCluster implements Cluster {

+

+    public final static String NAME = "failover";

+

+    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

+        return new FailoverClusterInvoker<T>(directory);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
new file mode 100644
index 0000000..b9bb6e1
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
@@ -0,0 +1,101 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+
+/**
+ * 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。
+ * 
+ * <a href="http://en.wikipedia.org/wiki/Failover">Failover</a>

+ * 
+ * @author william.liangf
+ */
+public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> {

+

+    private static final Logger logger = LoggerFactory.getLogger(FailoverClusterInvoker.class);

+
+    public FailoverClusterInvoker(Directory<T> directory) {
+        super(directory);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {

+        checkInvokers(invokers, invocation);

+        int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
+        if (len <= 0) {

+            len = 1;

+        }
+        // retry loop.
+        RpcException le = null; // last exception.
+        List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(invokers.size()); // invoked invokers.
+        Set<String> providers = new HashSet<String>(len);
+        for (int i = 0; i < len; i++) {
+            Invoker<T> invoker = select(loadbalance, invocation, invokers, invoked);
+            invoked.add(invoker);
+            RpcContext.getContext().setInvokers((List)invoked);
+            try {
+                Result result = invoker.invoke(invocation);

+                if (le != null && logger.isWarnEnabled()) {

+                    logger.warn("Although retry the method " + invocation.getMethodName()

+                            + " in the service " + getInterface().getName()

+                            + " was successful by the provider " + invoker.getUrl().getAddress()

+                            + ", but there have been failed providers " + providers 

+                            + " (" + providers.size() + "/" + invokers.size()

+                            + ") from the registry " + directory.getUrl().getAddress()

+                            + " on the consumer " + NetUtils.getLocalHost()

+                            + " using the dubbo version " + Version.getVersion() + ". Last error is: "

+                            + le.getMessage(), le);

+                }

+                return result;
+            } catch (RpcException e) {

+                if (e.isBiz()) { // biz exception.

+                    throw e;

+                }
+                le = e;

+            } catch (Throwable e) {
+                le = new RpcException(e.getMessage(), e);
+            } finally {

+                providers.add(invoker.getUrl().getAddress());

+            }
+        }
+        throw new RpcException(le != null ? le.getCode() : 0, "Failed to invoke the method "

+                + invocation.getMethodName() + " in the service " + getInterface().getName() 

+                + ". Tried " + len + " times of the providers " + providers 

+                + " (" + providers.size() + "/" + invokers.size() 

+                + ") from the registry " + directory.getUrl().getAddress()

+                + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "

+                + Version.getVersion() + ". Last error is: "

+                + (le != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le);
+    }

+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.java
new file mode 100644
index 0000000..972960f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+

+/**

+ * 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。 

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Fail-safe">Fail-safe</a>

+ * 

+ * @author william.liangf

+ */

+public class FailsafeCluster implements Cluster {

+

+    public final static String NAME = "failsafe";

+

+    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

+        return new FailsafeClusterInvoker<T>(directory);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
new file mode 100644
index 0000000..5ddebfa
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
@@ -0,0 +1,54 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;
+
+import java.util.List;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+
+/**
+ * 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。
+ * 
+ * <a href="http://en.wikipedia.org/wiki/Fail-safe">Fail-safe</a>
+ * 
+ * @author william.liangf
+ */
+public class FailsafeClusterInvoker<T> extends AbstractClusterInvoker<T>{
+    private static final Logger logger = LoggerFactory.getLogger(FailsafeClusterInvoker.class);
+    
+    public FailsafeClusterInvoker(Directory<T> directory) {
+        super(directory);
+    }
+    
+    public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+        try {

+            checkInvokers(invokers, invocation);

+            Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+            return invoker.invoke(invocation);
+        } catch (Throwable e) {
+            logger.error("Failsafe ignore exception: " + e.getMessage(), e);
+            return new RpcResult(); // ignore
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.java
new file mode 100644
index 0000000..f147efd
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+

+/**

+ * 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Fork_(topology)">Fork</a>

+ * 

+ * @author william.liangf

+ */

+public class ForkingCluster implements Cluster {

+    

+    public final static String NAME = "forking"; 

+    

+    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

+        return new ForkingClusterInvoker<T>(directory);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
new file mode 100644
index 0000000..1ee93ec
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
@@ -0,0 +1,99 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;

+import java.util.List;

+import java.util.concurrent.BlockingQueue;

+import java.util.concurrent.ExecutorService;

+import java.util.concurrent.Executors;

+import java.util.concurrent.LinkedBlockingQueue;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+
+/**
+ * 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Fork_(topology)">Fork</a>
+ * 
+ * @author william.liangf
+ */
+public class ForkingClusterInvoker<T> extends AbstractClusterInvoker<T>{
+

+    private final ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory("forking-cluster-timer", true)); 

+
+    public ForkingClusterInvoker(Directory<T> directory) {
+        super(directory);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {

+        checkInvokers(invokers, invocation);

+        final List<Invoker<T>> selected;
+        final int forks = getUrl().getParameter(Constants.FORKS_KEY, Constants.DEFAULT_FORKS);
+        final int timeout = getUrl().getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+        if (forks <= 0 || forks >= invokers.size()) {
+            selected = invokers;
+        } else {
+            selected = new ArrayList<Invoker<T>>();
+            for (int i = 0; i < forks; i++) {
+                //在invoker列表(排除selected)后,如果没有选够,则存在重复循环问题.见select实现.
+                Invoker<T> invoker = select(loadbalance, invocation, invokers, selected);
+                if(!selected.contains(invoker)){//防止重复添加invoker
+                    selected.add(invoker);
+                }
+            }
+        }

+        RpcContext.getContext().setInvokers((List)selected);
+        final AtomicInteger count = new AtomicInteger();
+        final BlockingQueue<Object> ref = new LinkedBlockingQueue<Object>();
+        for (final Invoker<T> invoker : selected) {
+            executor.execute(new Runnable() {
+                public void run() {
+                    try {
+                        Result result = invoker.invoke(invocation);
+                        ref.offer(result);
+                    } catch(Throwable e) {
+                        int value = count.incrementAndGet();
+                        if (value >= selected.size()) {
+                            ref.offer(e);
+                        }
+                    }
+                }
+            });
+        }
+        try {
+            Object ret = ref.poll(timeout, TimeUnit.MILLISECONDS);

+            if (ret instanceof Throwable) {

+                Throwable e = (Throwable) ret;

+                throw new RpcException(e instanceof RpcException ? ((RpcException)e).getCode() : 0, "Failed to forking invoke provider " + selected + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e.getCause() != null ? e.getCause() : e);

+            }

+            return (Result) ret;
+        } catch (InterruptedException e) {
+            throw new RpcException("Failed to forking invoke provider " + selected + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableCluster.java
new file mode 100644
index 0000000..29ebbc4
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableCluster.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MergeableCluster implements Cluster {
+
+    public static final String NAME = "mergeable";
+
+    public <T> Invoker<T> join( Directory<T> directory ) throws RpcException {
+        return new MergeableClusterInvoker<T>( directory );
+    }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvoker.java
new file mode 100644
index 0000000..dc16069
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvoker.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.merger.ArrayMerger;
+import com.alibaba.dubbo.rpc.cluster.merger.ListMerger;
+import com.alibaba.dubbo.rpc.cluster.merger.MapMerger;
+import com.alibaba.dubbo.rpc.cluster.merger.SetMerger;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MergeableClusterInvoker<T> implements Invoker<T> {
+
+    private ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory("mergeable-cluster-executor", true));
+    
+    private final Directory<T> directory;
+
+    public MergeableClusterInvoker(Directory<T> directory) {
+        this.directory = directory;
+    }
+
+    public Result invoke(final Invocation invocation) throws RpcException {
+        int timeout = getUrl().getMethodParameter( invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT );
+        List<Invoker<T>> invokers = directory.list(invocation);
+        
+        Map<String, Future<Result>> results = new HashMap<String, Future<Result>>();
+        for( final Invoker<T> invoker : invokers ) {
+            Future<Result> future = executor.submit( new Callable<Result>() {
+                public Result call() throws Exception {
+                    return invoker.invoke(new RpcInvocation(invocation, invoker));
+                }
+            } );
+            results.put( invoker.getUrl().getServiceKey(), future );
+        }
+
+        Object result = null;
+        Class<?> returnType;
+        try {
+            returnType = getInterface().getMethod(
+                    invocation.getMethodName(), invocation.getParameterTypes() ).getReturnType();
+        } catch ( NoSuchMethodException e ) {
+            throw new RpcException( e.getMessage(), e );
+        }
+
+        List<Result> resultList = new ArrayList<Result>( results.size() );
+        
+        for ( Map.Entry<String, Future<Result>> entry : results.entrySet() ) {
+            Future<Result> future = entry.getValue();
+            try {
+                resultList.add( future.get( timeout, TimeUnit.MILLISECONDS ) );
+            } catch ( Exception e ) {
+                throw new RpcException( new StringBuilder( 32 )
+                                                .append( "Failed to invoke service " )
+                                                .append( entry.getKey() )
+                                                .append( ": " )
+                                                .append( e.getMessage() ).toString(),
+                                        e );
+            }
+        }
+
+        if ( returnType != void.class && resultList.size() > 0 ) {
+            String merger = getUrl().getMethodParameter( invocation.getMethodName(), Constants.MERGER_KEY );
+            if ( merger != null && !"".equals( merger.trim() ) ) {
+                Method method;
+                try {
+                    method = returnType.getMethod( merger, returnType );
+                } catch ( NoSuchMethodException e ) {
+                    throw new RpcException( new StringBuilder( 32 )
+                                                    .append( "Can not merge result because missing method [ " )
+                                                    .append( merger )
+                                                    .append( " ] in class [ " )
+                                                    .append( returnType.getClass().getName() )
+                                                    .append( " ]" )
+                                                    .toString() );
+                }
+                if ( method != null ) {
+                    if ( !Modifier.isPublic( method.getModifiers() ) ) {
+                        method.setAccessible( true );
+                    }
+                    result = resultList.remove( 0 ).getValue();
+                    try {
+                        if ( method.getReturnType() != void.class
+                                && method.getReturnType().isAssignableFrom( result.getClass() ) ) {
+                            for ( Result r : resultList ) {
+                                result = method.invoke( result, r.getValue() );
+                            }
+                        } else {
+                            for ( Result r : resultList ) {
+                                method.invoke( result, r.getValue() );
+                            }
+                        }
+                    } catch ( Exception e ) {
+                        throw new RpcException( 
+                                new StringBuilder( 32 )
+                                        .append( "Can not merge result: " )
+                                        .append( e.getMessage() ).toString(), 
+                                e );
+                    }
+                } else {
+                    throw new RpcException(
+                            new StringBuilder( 32 )
+                                    .append( "Can not merge result because missing method [ " )
+                                    .append( merger )
+                                    .append( " ] in class [ " )
+                                    .append( returnType.getClass().getName() )
+                                    .append( " ]" )
+                                    .toString() );
+                }
+            } else if ( List.class.isAssignableFrom( returnType ) ) {
+                List<List<?>> args = new ArrayList<List<?>>();
+                for( Result r : resultList ) {
+                    args.add( ( List<?> ) r.getValue() );
+                }
+                result = ListMerger.INSTANCE.merge( args.toArray( new List[ args.size() ] ) );
+            } else if ( Set.class.isAssignableFrom( returnType ) ) {
+                List<Set<?>> args = new ArrayList<Set<?>>();
+                for( Result r : resultList ) {
+                    args.add( ( Set<?> ) r.getValue() );
+                }
+                result = SetMerger.INSTANCE.merge( args.toArray( new Set[args.size()] ) );
+            } else if ( Map.class.isAssignableFrom( returnType ) ) {
+                List<Map<?,?>> args = new ArrayList<Map<?,?>>();
+                for( Result r : resultList ) {
+                    args.add( ( Map<?,?> ) r.getValue() );
+                }
+                result = MapMerger.INSTANCE.merge( args.toArray( new Map[args.size()] ) );
+            } else if ( returnType.isArray() ) {
+                List<Object> args = new ArrayList<Object>();
+                for( Result r : resultList ) {
+                    args.add( r.getValue() );
+                }
+                result = ArrayMerger.INSTANCE.merge( args.toArray( new Object[args.size()] ) );
+            } else {
+                throw new RpcException( "There is no merger to merge result." );
+            }
+        }
+
+        return new RpcResult( result );
+    }
+
+    public Class<T> getInterface() {
+        return directory.getInterface();
+    }
+
+    public URL getUrl() {
+        return directory.getUrl();
+    }
+
+    public boolean isAvailable() {
+        return directory.isAvailable();
+    }
+
+    public void destroy() {
+        directory.destroy();
+    }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java
new file mode 100644
index 0000000..8d77aa5
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java
@@ -0,0 +1,151 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support.wrapper;
+
+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.support.MockInvoker;

+
+/**
+ * @author chao.liuc
+ */
+public class MockClusterInvoker<T> implements Invoker<T>{

+	

+	private static final Logger logger = LoggerFactory.getLogger(MockClusterInvoker.class);

+

+	private final Directory<T> directory ;

+	

+	private final Invoker<T> invoker;

+
+    public MockClusterInvoker(Directory<T> directory, Invoker<T> invoker) {
+       	this.directory = directory;

+       	this.invoker = invoker;
+    }
+

+	public URL getUrl() {

+		return directory.getUrl();

+	}

+

+	public boolean isAvailable() {

+		return directory.isAvailable();

+	}

+

+	public void destroy() {

+		this.invoker.destroy();

+	}

+

+	public Class<T> getInterface() {

+		return directory.getInterface();

+	}

+

+	public Result invoke(Invocation invocation) throws RpcException {

+		Result result = null;

+        

+        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim(); 

+        if (value.length() == 0 || value.equalsIgnoreCase("false")){

+        	//no mock

+        	result = this.invoker.invoke(invocation);

+        } else if (value.startsWith("force")) {

+        	if (logger.isWarnEnabled()) {

+        		logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " +  directory.getUrl());

+        	}

+        	//force:direct mock

+        	result = doMockInvoke(invocation, null);

+        } else {

+        	//fail-mock

+        	try {

+        		result = this.invoker.invoke(invocation);

+        	}catch (RpcException e) {

+				if (e.isBiz()) {

+					throw e;

+				} else {

+					if (logger.isWarnEnabled()) {

+		        		logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " +  directory.getUrl(), e);

+		        	}

+					result = doMockInvoke(invocation, e);

+				}

+			}

+        }

+        return result;

+	}

+	

+	@SuppressWarnings({ "unchecked", "rawtypes" })

+	private Result doMockInvoke(Invocation invocation,RpcException e){

+		Result result = null;

+    	Invoker<T> minvoker ;

+    	

+    	List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);

+		if (mockInvokers == null || mockInvokers.size() == 0){

+			minvoker = (Invoker<T>) new MockInvoker(directory.getUrl());

+		} else {

+			minvoker = mockInvokers.get(0);

+		}

+		try {

+			result = minvoker.invoke(invocation);

+		} catch (RpcException me) {

+			if (me.isBiz()) {

+				result = new RpcResult(me.getCause());

+			} else {

+				throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());

+			}

+//			

+		} catch (Throwable me) {

+			throw new RpcException(getMockExceptionMessage(e, me), me.getCause());

+		}

+		return result;

+    }

+	

+	private String getMockExceptionMessage(Throwable t, Throwable mt){

+		String msg = "mock error : " + mt.getMessage();

+		if (t != null){

+			msg = msg + ", invoke error is :" + StringUtils.toString(t);

+		}

+		return msg;

+	}

+

+	/**

+     * 返回MockInvoker

+     * 契约:

+     * directory根据invocation中是否有Constants.INVOCATION_NEED_MOCK,来判断获取的是一个normal invoker 还是一个 mock invoker

+     * 如果directorylist 返回多个mock invoker,只使用第一个invoker.

+     * @param invocation

+     * @return 

+     */

+    private List<Invoker<T>> selectMockInvoker(Invocation invocation){

+    	//TODO generic invoker?

+        if (invocation instanceof RpcInvocation){

+            //存在隐含契约(虽然在接口声明中增加描述,但扩展性会存在问题.同时放在attachement中的做法需要改进

+        	((RpcInvocation)invocation).setAttachment(Constants.INVOCATION_NEED_MOCK, Boolean.TRUE.toString());

+            //directory根据invocation中attachment是否有Constants.INVOCATION_NEED_MOCK,来判断获取的是normal invokers or mock invokers

+        	List<Invoker<T>> invokers = directory.list(invocation);

+            return invokers;

+        } else {

+            return null ;

+        }

+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
new file mode 100644
index 0000000..065dcef
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
@@ -0,0 +1,42 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support.wrapper;

+

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+

+/**

+ * mock impl

+ * 

+ * @author chao.liuc

+ * 

+ */

+public class MockClusterWrapper implements Cluster {

+

+	private Cluster cluster;

+

+	public MockClusterWrapper(Cluster cluster) {

+		this.cluster = cluster;

+	}

+

+	public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

+		return new MockClusterInvoker<T>(directory,

+				this.cluster.join(directory));

+	}

+

+}

diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster
new file mode 100644
index 0000000..54e85ed
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster
@@ -0,0 +1,9 @@
+mock=com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper

+failover=com.alibaba.dubbo.rpc.cluster.support.FailoverCluster

+failfast=com.alibaba.dubbo.rpc.cluster.support.FailfastCluster

+failsafe=com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster

+failback=com.alibaba.dubbo.rpc.cluster.support.FailbackCluster

+forking=com.alibaba.dubbo.rpc.cluster.support.ForkingCluster

+available=com.alibaba.dubbo.rpc.cluster.support.AvailableCluster

+mergeable=com.alibaba.dubbo.rpc.cluster.support.MergeableCluster

+broadcast=com.alibaba.dubbo.rpc.cluster.support.BroadcastCluster
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance
new file mode 100644
index 0000000..b5aa6db
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance
@@ -0,0 +1,5 @@
+adptive=com.alibaba.dubbo.rpc.cluster.loadbalance.LoadBalanceAdptive

+random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance

+roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance

+leastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance

+consistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger
new file mode 100644
index 0000000..9b56a3e
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.rpc.cluster.merger.ArrayMerger

+com.alibaba.dubbo.rpc.cluster.merger.ListMerger

+com.alibaba.dubbo.rpc.cluster.merger.SetMerger

+com.alibaba.dubbo.rpc.cluster.merger.MapMerger
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory
new file mode 100644
index 0000000..cbf1a42
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory
@@ -0,0 +1,2 @@
+file=com.alibaba.dubbo.rpc.cluster.router.FileRouterFactory

+script=com.alibaba.dubbo.rpc.cluster.router.ScriptRouterFactory
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.java
new file mode 100644
index 0000000..caab6af
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.java
@@ -0,0 +1,164 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+@SuppressWarnings("unchecked")
+public class StickyTest {
+
+    List<Invoker<StickyTest>> invokers = new ArrayList<Invoker<StickyTest>>();
+    
+    
+    Invoker<StickyTest> invoker1 = EasyMock.createMock(Invoker.class);
+    Invoker<StickyTest> invoker2 = EasyMock.createMock(Invoker.class);
+    Invocation invocation;
+    Directory<StickyTest> dic ;
+    Result result = new RpcResult();
+    StickyClusterInvoker<StickyTest> clusterinvoker = null;
+    
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        dic = EasyMock.createMock(Directory.class);
+        invocation = EasyMock.createMock(Invocation.class);
+        
+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+        EasyMock.expect(dic.getInterface()).andReturn(StickyTest.class).anyTimes();
+        EasyMock.replay(dic);
+        invokers.add(invoker1);
+        invokers.add(invoker2);
+        
+        clusterinvoker = new StickyClusterInvoker<StickyTest>(dic);
+    }
+    URL url = URL.valueOf("test://test:11/test?" 
+            +"&loadbalance=roundrobin"
+//            +"&"+Constants.CLUSTER_AVAILABLE_CHECK_KEY+"=true"
+            +"&"+Constants.CLUSTER_STICKY_KEY+"=true"
+            );
+    
+    int runs = 1;
+    @Test
+    public void testStickyNoCheck() {
+        int count = testSticky(null,false);
+        System.out.println(count);
+        Assert.assertTrue(count>0 && count <=runs);
+    }
+    
+    @Test
+    public void testStickyForceCheck() {
+        int count = testSticky(null,true);
+        Assert.assertTrue(count == 0 || count  == runs);
+    }
+    @Test
+    public void testMethodStickyNoCheck() {
+        int count = testSticky("method1",false);
+        System.out.println(count);
+        Assert.assertTrue(count>0 && count <=runs);
+    }
+    
+    @Test
+    public void testMethodStickyForceCheck() {
+        int count = testSticky("method1",true);
+        Assert.assertTrue(count == 0 || count  == runs);
+    }
+    
+    @Test
+    public void testMethodsSticky() {
+        for(int i = 0 ;i<100 ; i++){//多次调用看两个方法是否都选在同一个invoker
+            int count1 = testSticky("method1",true);
+            int count2 = testSticky("method2",true);
+            Assert.assertTrue(count1 == count2);
+        }
+    }
+    
+    public int testSticky(String methodName, boolean check) {
+        if (methodName == null){
+            url = url.addParameter(Constants.CLUSTER_STICKY_KEY, String.valueOf(check));
+        }else {
+            url = url.addParameter(methodName+"."+Constants.CLUSTER_STICKY_KEY, String.valueOf(check));
+        }
+        EasyMock.reset(invoker1);
+        EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+        EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(StickyTest.class).anyTimes();
+        EasyMock.replay(invoker1);
+        
+        EasyMock.reset(invoker2);
+        EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+        EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker2.getInterface()).andReturn(StickyTest.class).anyTimes();
+        EasyMock.replay(invoker2);
+        
+        EasyMock.reset(invocation);
+        EasyMock.expect(invocation.getMethodName()).andReturn(methodName).anyTimes();
+        EasyMock.replay(invocation);
+        
+        int count = 0;
+        for (int i = 0; i < runs; i++) {
+            Assert.assertEquals(null, clusterinvoker.invoke(invocation));
+            if(invoker1 == clusterinvoker.getSelectedInvoker()){
+                count ++;
+            }
+        }
+        return count;
+    }
+    
+    
+    static class StickyClusterInvoker<T> extends  AbstractClusterInvoker<T>{
+        private Invoker<T> selectedInvoker ;
+        public StickyClusterInvoker(Directory<T> directory) {
+            super(directory);
+        }
+        public StickyClusterInvoker(Directory<T> directory,URL url) {
+            super(directory, url);
+        }
+        @Override
+        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
+                                  LoadBalance loadbalance) throws RpcException {
+            Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+            selectedInvoker = invoker ;
+            return null;
+        }
+        public Invoker<T> getSelectedInvoker() {
+            return selectedInvoker;
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoService.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoService.java
new file mode 100644
index 0000000..373e7d1
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoService.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.filter;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+	String sayHello(String name);
+
+	int plus(int a,int b);
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceLocal.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceLocal.java
new file mode 100644
index 0000000..8f29028
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceLocal.java
@@ -0,0 +1,42 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.filter;

+

+/**

+ * <code>TestService</code>

+ */

+

+public class DemoServiceLocal implements DemoService {

+

+    public DemoServiceLocal(DemoService demoService){

+    }

+

+    public String sayHello(String name) {

+        return name;

+    }

+

+    public int plus(int a, int b) {

+        return a + b;

+    }

+    

+    public void ondisconnect(){

+        

+    }

+    

+    public void onconnect(){

+        

+    }

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceMock.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceMock.java
new file mode 100644
index 0000000..b1b7b4a
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceMock.java
@@ -0,0 +1,30 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.filter;

+

+/**

+ * MockService.java

+ * @author tony.chenl

+ */

+public class DemoServiceMock implements DemoService{

+    public String sayHello(String name) {

+        return name;

+    }

+

+    public int plus(int a, int b) {

+        return a+b;

+    }

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceStub.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceStub.java
new file mode 100644
index 0000000..c9bcb6c
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceStub.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.filter;

+

+/**

+ * <code>TestService</code>

+ */

+

+public class DemoServiceStub implements DemoService {

+

+    public DemoServiceStub(DemoService demoService){

+    }

+

+    public String sayHello(String name) {

+        return name;

+    }

+

+    public int plus(int a, int b) {

+        return a + b;

+    }

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/MockService.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/MockService.java
new file mode 100644
index 0000000..a8a2d3a
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/MockService.java
@@ -0,0 +1,30 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.filter;

+

+/**

+ * MockService.java

+ * @author tony.chenl

+ */

+public class MockService implements DemoService{

+    public String sayHello(String name) {

+        return name;

+    }

+

+    public int plus(int a, int b) {

+        return a+b;

+    }

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
new file mode 100644
index 0000000..c56cb61
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
@@ -0,0 +1,150 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.loadbalance;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * RoundRobinLoadBalanceTest
+ * @author liuchao
+ *
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public class LoadBalanceTest {
+    Invocation invocation ;
+    List<Invoker<LoadBalanceTest>> invokers = new ArrayList<Invoker<LoadBalanceTest>>();
+    Invoker<LoadBalanceTest> invoker1 ;
+    Invoker<LoadBalanceTest> invoker2 ;
+    Invoker<LoadBalanceTest> invoker3 ;
+    Invoker<LoadBalanceTest> invoker4 ;
+    Invoker<LoadBalanceTest> invoker5 ;
+    /**
+     * @throws java.lang.Exception
+     */
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+
+    /**
+     * @throws java.lang.Exception
+     */
+    @Before
+    public void setUp() throws Exception {
+
+        
+        invocation = EasyMock.createMock(Invocation.class);
+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+        
+        invoker1 = EasyMock.createMock(Invoker.class);
+        invoker2 = EasyMock.createMock(Invoker.class);
+        invoker3 = EasyMock.createMock(Invoker.class);
+        invoker4 = EasyMock.createMock(Invoker.class);
+        invoker5 = EasyMock.createMock(Invoker.class);
+        
+        URL url = URL.valueOf("test://127.0.0.1/DemoService");
+        
+        EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+        
+        EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker2.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+        EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+        
+        EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker3.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+        EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();
+        
+        EasyMock.expect(invoker4.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker4.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+        EasyMock.expect(invoker4.getUrl()).andReturn(url).anyTimes();
+        
+        EasyMock.expect(invoker5.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker5.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+        EasyMock.expect(invoker5.getUrl()).andReturn(url).anyTimes();
+        
+        EasyMock.replay(invocation,invoker1,invoker2,invoker3,invoker4,invoker5);
+        
+        invokers.add(invoker1);
+        invokers.add(invoker2);
+        invokers.add(invoker3);
+        invokers.add(invoker4);
+        invokers.add(invoker5);
+    }
+
+    @Test
+    public void testRoundRobinLoadBalance_select() {
+        int runs = 10000;
+        Map<Invoker,AtomicLong> counter = getInvokeCounter(runs,RoundRobinLoadBalance.NAME);
+        for (Invoker minvoker :counter.keySet() ){
+            Long count = counter.get(minvoker).get();
+            Assert.assertTrue("abs diff shoud < 1", Math.abs(count-runs/(0f+invokers.size())) <1f);
+        }
+    }
+    @Test
+    public void testRandomLoadBalance_select() {
+        int runs = 1000;
+        Map<Invoker,AtomicLong> counter = getInvokeCounter(runs,RandomLoadBalance.NAME);
+        for (Invoker minvoker :counter.keySet() ){
+            Long count = counter.get(minvoker).get();
+//            System.out.println(count);
+            Assert.assertTrue("abs diff shoud < avg", Math.abs(count-runs/(0f+invokers.size())) <runs/(0f+invokers.size()));
+        }
+    }
+    @Test
+    public void testLeastActiveLoadBalance_select() {
+        int runs = 10000;
+        Map<Invoker,AtomicLong> counter = getInvokeCounter(runs,LeastActiveLoadBalance.NAME);
+        for (Invoker minvoker :counter.keySet() ){
+            Long count = counter.get(minvoker).get();
+//            System.out.println(count);
+            Assert.assertTrue("abs diff shoud < avg", Math.abs(count-runs/(0f+invokers.size())) <runs/(0f+invokers.size()));
+        }
+    }
+    
+    
+    public Map<Invoker,AtomicLong> getInvokeCounter(int runs,String loadbalanceName) {
+        Map<Invoker,AtomicLong> counter = new ConcurrentHashMap<Invoker,AtomicLong>();
+        LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName);
+        for(Invoker invoker :invokers){
+            counter.put(invoker, new AtomicLong(0));
+        }
+        for(int i=0;i<runs;i++){
+            Invoker sinvoker = lb.select(invokers, invocation);
+            counter.get(sinvoker).incrementAndGet();
+        }
+        return counter;
+    }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/merger/ResultMergerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/merger/ResultMergerTest.java
new file mode 100644
index 0000000..06eadb9
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/merger/ResultMergerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.cluster.merger;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class ResultMergerTest {
+
+    @Test
+    public void testListMerger() throws Exception {
+        List<Object> list1 = new ArrayList<Object>();
+        list1.add( null );
+        list1.add( "1" );
+        list1.add( "2" );
+        List<Object> list2 = new ArrayList<Object>();
+        list2.add( "3" );
+        list2.add( "4" );
+        
+        List result = ListMerger.INSTANCE.merge( list1, list2 );
+        Assert.assertEquals(5, result.size());
+        Assert.assertEquals( new ArrayList<String>(){
+            {
+                add( null );
+                add( "1" );
+                add( "2" );
+                add( "3" );
+                add( "4" );
+            }
+        }, result);
+    }
+    
+    @Test
+    public void testSetMerger() throws Exception {
+        Set<Object> set1 = new HashSet<Object>();
+        set1.add( null );
+        set1.add( "1" );
+        set1.add( "2" );
+        Set<Object> set2 = new HashSet<Object>();
+        set2.add( "2" );
+        set2.add( "3" );
+        
+        Set result = SetMerger.INSTANCE.merge( set1, set2 );
+        
+        Assert.assertEquals( 4, result.size() );
+        Assert.assertEquals( new HashSet<String>(){
+            {
+                add( null );
+                add( "1" );
+                add( "2" );
+                add( "3" );
+            }
+        }, result);
+    }
+
+    @Test
+    public void testArrayMerger() throws Exception {
+        String[] stringArray1 = {"1", "2", "3"};
+        String[] stringArray2 = {"4", "5", "6"};
+        String[] stringArray3 = {};
+
+        Object result = ArrayMerger.INSTANCE.merge(stringArray1, stringArray2, stringArray3);
+        Assert.assertTrue(result.getClass().isArray());
+        Assert.assertEquals(6, Array.getLength(result));
+        Assert.assertTrue(String.class.isInstance(Array.get(result, 0)));
+        for(int i = 0; i < 6; i++) {
+            Assert.assertEquals(String.valueOf(i + 1), Array.get(result, i));
+        }
+        
+        int[] intArray1 = {1, 2, 3};
+        int[] intArray2 = {4, 5, 6};
+        int[] intArray3 = {7};
+        result = ArrayMerger.INSTANCE.merge(intArray1, intArray2, intArray3);
+        Assert.assertTrue(result.getClass().isArray());
+        Assert.assertEquals(7, Array.getLength(result));
+        Assert.assertTrue(int.class == result.getClass().getComponentType());
+        for (int i = 0; i < 7; i++) {
+            Assert.assertEquals(i + 1, Array.get(result, i));
+        }
+        
+        try {
+            ArrayMerger.INSTANCE.merge( intArray1, intArray1, "Hello" );
+            Assert.fail();
+        } catch ( Exception e ) {
+            // ignore
+        }
+        
+    }
+}
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/MockInvoker.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/MockInvoker.java
new file mode 100644
index 0000000..9e2c8f1
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/MockInvoker.java
@@ -0,0 +1,56 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.router;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+public class MockInvoker<T> implements Invoker<T> {
+    private boolean available = false;
+    private URL url ;
+
+    public MockInvoker() {
+    }
+    public MockInvoker(URL url) {
+        super();
+        this.url = url;
+    }
+    public MockInvoker(boolean available) {
+        this.available = available;
+    }
+
+    public Class<T> getInterface() {
+        return null;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    public boolean isAvailable() {
+        return available;
+    }
+
+    public Result invoke(Invocation invocation) throws RpcException {
+        return null;
+    }
+
+    public void destroy() {
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterEngineTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterEngineTest.java
new file mode 100644
index 0000000..4a2ac3b
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterEngineTest.java
@@ -0,0 +1,86 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.router;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Router;
+
+public class ScriptRouterEngineTest {
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() throws Exception {
+    }
+    
+    private URL SCRIPT_URL = URL.valueOf("script://javascript?type=javascript");
+    
+    private URL getRouteUrl(String rule) {
+        return SCRIPT_URL.addParameterAndEncoded(Constants.RULE_KEY, rule);
+    }
+    
+    @Test
+    public void testRoute_ReturnAll(){
+        Router router = new ScriptRouterFactory().getRouter(getRouteUrl("function route(op1,op2){return op1} route(invokers)"));
+        List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+        invokers.add(new MockInvoker<String>());
+        invokers.add(new MockInvoker<String>());
+        invokers.add(new MockInvoker<String>());
+        List<Invoker<String>> fileredInvokers = router.route(invokers, new RpcInvocation());
+        Assert.assertEquals(invokers, fileredInvokers);
+    }
+    
+    @Test
+    public void testRoute_PickInvokers(){
+        String rule = "var result = new java.util.ArrayList(invokers.size());" +
+                		"for (i=0;i<invokers.size(); i++){ " +
+                		    "if (invokers.get(i).isAvailable()) {" +
+                		        "result.add(invokers.get(i)) ;" +
+                		    "}" +
+                		"} ; " +
+                		"return result;";
+        String script = "function route(invokers,invocation,context){" + rule + "} route(invokers,invocation,context)";
+        Router router = new ScriptRouterFactory().getRouter(getRouteUrl(script));
+        
+        List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+        Invoker<String> invoker1 = new MockInvoker<String>(false) ;
+        Invoker<String> invoker2 = new MockInvoker<String>(true) ;
+        Invoker<String> invoker3 = new MockInvoker<String>(true) ;
+        invokers.add(invoker1);
+        invokers.add(invoker2);
+        invokers.add(invoker3);
+        List<Invoker<String>> fileredInvokers = router.route(invokers, new RpcInvocation());
+        Assert.assertEquals(2, fileredInvokers.size());
+        Assert.assertEquals(invoker2, fileredInvokers.get(0));
+        Assert.assertEquals(invoker3, fileredInvokers.get(1));
+    }
+    //TODO 异常场景测试。
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
new file mode 100644
index 0000000..b376c7c
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
@@ -0,0 +1,194 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.router.file;
+
+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+

+import javax.script.ScriptEngineManager;

+

+import junit.framework.Assert;

+

+import org.easymock.EasyMock;

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+import com.alibaba.dubbo.rpc.cluster.RouterFactory;

+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;

+import com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;

+
+/**
+ * @author chao.liuc
+ */
+@SuppressWarnings("unchecked")
+public class FileRouterEngineTest {
+    List<Invoker<FileRouterEngineTest>> invokers = new ArrayList<Invoker<FileRouterEngineTest>>();
+
+    Invoker<FileRouterEngineTest>       invoker1 = EasyMock.createMock(Invoker.class);
+    Invoker<FileRouterEngineTest>       invoker2 = EasyMock.createMock(Invoker.class);
+    Invocation                          invocation;
+    Directory<FileRouterEngineTest>     dic;
+    Result                              result   = new RpcResult();
+    private RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+    
+    private static boolean isScriptUnsupported = new ScriptEngineManager().getEngineByName("javascript") == null;
+
+    @Before
+    public void setUp() throws Exception {
+        invokers.add(invoker1);
+        invokers.add(invoker2);
+    }
+
+    @Test
+    public void testRouteNotAvailable() {
+        if (isScriptUnsupported) return;
+        URL url = initUrl("notAvailablerule.javascript");
+        initInvocation("method1");
+        initDic(url);
+        initInvokers(url, true, false);
+
+        MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+                dic, url);
+        for (int i = 0; i < 100; i++) {
+            sinvoker.invoke(invocation);
+            Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+            Assert.assertEquals(invoker2, invoker);
+        }
+    }
+
+    @Test
+    public void testRouteAvailable() {
+        if (isScriptUnsupported) return;
+        URL url = initUrl("availablerule.javascript");
+        initInvocation("method1");
+        initDic(url);
+        initInvokers(url);
+
+        MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+                dic, url);
+        for (int i = 0; i < 100; i++) {
+            sinvoker.invoke(invocation);
+            Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+            Assert.assertEquals(invoker1, invoker);
+        }
+    }
+
+    @Test
+    public void testRouteByMethodName() {
+        if (isScriptUnsupported) return;
+        URL url = initUrl("methodrule.javascript");
+        {
+            initInvocation("method1");
+            initDic(url);
+            initInvokers(url, true, true);
+
+            MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+                    dic, url);
+            for (int i = 0; i < 100; i++) {
+                sinvoker.invoke(invocation);
+                Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+                Assert.assertEquals(invoker1, invoker);
+            }
+        }
+        {
+            initInvocation("method2");
+            initDic(url);
+            initInvokers(url, true, true);
+            MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+                    dic, url);
+            for (int i = 0; i < 100; i++) {
+                sinvoker.invoke(invocation);
+                Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+                Assert.assertEquals(invoker2, invoker);
+            }
+        }
+    }
+
+    private URL initUrl(String filename) {
+        filename = getClass().getClassLoader().getResource(getClass().getPackage().getName().replace('.', '/') + "/" + filename).toString();
+        URL url = URL.valueOf(filename);
+        return url;
+    }
+
+    private void initInvocation(String methodName) {
+        invocation = new RpcInvocation();

+        ((RpcInvocation)invocation).setMethodName(methodName);
+    }
+
+    private void initInvokers(URL url) {
+        initInvokers(url, true, false);
+    }
+
+    private void initInvokers(URL url, boolean invoker1Status, boolean invoker2Status) {
+        EasyMock.reset(invoker1);
+        EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+        EasyMock.expect(invoker1.isAvailable()).andReturn(invoker1Status).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(FileRouterEngineTest.class).anyTimes();
+        EasyMock.replay(invoker1);
+
+        EasyMock.reset(invoker2);
+        EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+        EasyMock.expect(invoker2.isAvailable()).andReturn(invoker2Status).anyTimes();
+        EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker2.getInterface()).andReturn(FileRouterEngineTest.class).anyTimes();
+        EasyMock.replay(invoker2);
+    }
+
+    private void initDic(URL url) {
+        dic = new StaticDirectory<FileRouterEngineTest>(url, invokers, Arrays.asList(routerFactory.getRouter(url)));
+    }
+
+    static class MockClusterInvoker<T> extends AbstractClusterInvoker<T> {
+        private Invoker<T> selectedInvoker;
+
+        public MockClusterInvoker(Directory<T> directory) {
+            super(directory);
+        }
+
+        public MockClusterInvoker(Directory<T> directory, URL url) {
+            super(directory, url);
+        }
+
+        @Override
+        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
+                                  LoadBalance loadbalance) throws RpcException {
+            Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+            selectedInvoker = invoker;
+            return null;
+        }
+
+        public Invoker<T> getSelectedInvoker() {
+            return selectedInvoker;
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
new file mode 100644
index 0000000..65a78bd
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
@@ -0,0 +1,469 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.atomic.AtomicLong;

+

+import junit.framework.Assert;

+

+import org.easymock.EasyMock;

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;

+import com.alibaba.dubbo.rpc.cluster.filter.DemoService;

+import com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance;

+import com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;

+import com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;

+
+/**
+ * AbstractClusterInvokerTest
+ * @author chao.liuc
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class AbstractClusterInvokerTest {
+    List<Invoker<IHelloService>> invokers = new ArrayList<Invoker<IHelloService>>();
+    List<Invoker<IHelloService>> selectedInvokers = new ArrayList<Invoker<IHelloService>>();
+    AbstractClusterInvoker<IHelloService> cluster;
+    AbstractClusterInvoker<IHelloService> cluster_nocheck;

+    Directory<IHelloService> dic ;
+    RpcInvocation invocation = new RpcInvocation();

+    URL url = URL.valueOf("registry://localhost:9090");
+    
+    Invoker<IHelloService> invoker1 ;
+    Invoker<IHelloService> invoker2 ;
+    Invoker<IHelloService> invoker3 ;
+    Invoker<IHelloService> invoker4 ;
+    Invoker<IHelloService> invoker5 ;

+    Invoker<IHelloService> mockedInvoker1 ;
+    
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+    @SuppressWarnings({ "unchecked" })
+    @Before
+    public void setUp() throws Exception {
+    	invocation.setMethodName("sayHello");

+        
+        invoker1 = EasyMock.createMock(Invoker.class);
+        invoker2 = EasyMock.createMock(Invoker.class);
+        invoker3 = EasyMock.createMock(Invoker.class);
+        invoker4 = EasyMock.createMock(Invoker.class);
+        invoker5 = EasyMock.createMock(Invoker.class);

+        mockedInvoker1 = EasyMock.createMock(Invoker.class);
+        
+        URL turl = URL.valueOf("test://test:11/test");
+        
+        EasyMock.expect(invoker1.isAvailable()).andReturn(false).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(IHelloService.class).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(turl.addParameter("name", "invoker1")).anyTimes();
+        
+        EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker2.getInterface()).andReturn(IHelloService.class).anyTimes();
+        EasyMock.expect(invoker2.getUrl()).andReturn(turl.addParameter("name", "invoker2")).anyTimes();
+        
+        EasyMock.expect(invoker3.isAvailable()).andReturn(false).anyTimes();
+        EasyMock.expect(invoker3.getInterface()).andReturn(IHelloService.class).anyTimes();
+        EasyMock.expect(invoker3.getUrl()).andReturn(turl.addParameter("name", "invoker3")).anyTimes();
+        
+        EasyMock.expect(invoker4.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker4.getInterface()).andReturn(IHelloService.class).anyTimes();
+        EasyMock.expect(invoker4.getUrl()).andReturn(turl.addParameter("name", "invoker4")).anyTimes();
+        
+        EasyMock.expect(invoker5.isAvailable()).andReturn(false).anyTimes();
+        EasyMock.expect(invoker5.getInterface()).andReturn(IHelloService.class).anyTimes();
+        EasyMock.expect(invoker5.getUrl()).andReturn(turl.addParameter("name", "invoker5")).anyTimes();

+        

+        EasyMock.expect(mockedInvoker1.isAvailable()).andReturn(false).anyTimes();

+        EasyMock.expect(mockedInvoker1.getInterface()).andReturn(IHelloService.class).anyTimes();

+        EasyMock.expect(mockedInvoker1.getUrl()).andReturn(turl.setProtocol("mock")).anyTimes();
+        
+        EasyMock.replay(invoker1,invoker2,invoker3,invoker4,invoker5,mockedInvoker1);
+        

+        invokers.add(invoker1);

+        dic = new StaticDirectory<IHelloService>(url, invokers, null);

+        cluster = new AbstractClusterInvoker(dic) {
+            @Override
+            protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance)
+                    throws RpcException {
+                return null;
+            }
+        };
+        
+        cluster_nocheck = new AbstractClusterInvoker(dic,url.addParameterIfAbsent(Constants.CLUSTER_AVAILABLE_CHECK_KEY, Boolean.FALSE.toString())) {
+            @Override
+            protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance)
+                    throws RpcException {
+                return null;
+            }
+        };

+        

+    }
+    
+    @Test
+    public void testSelect_Invokersize0() throws Exception {
+        {
+            Invoker invoker = cluster.select(null,null,null,null);
+            Assert.assertEquals(null, invoker);
+        }
+        {
+            invokers.clear();
+            selectedInvokers.clear();
+            Invoker invoker = cluster.select(null,null,invokers,null);
+            Assert.assertEquals(null, invoker);
+        }
+    }
+    
+    @Test
+    public void testSelect_Invokersize1() throws Exception {
+        invokers.clear();
+        invokers.add(invoker1);
+        Invoker invoker = cluster.select(null,null,invokers,null);
+        Assert.assertEquals(invoker1, invoker);
+    }
+    
+    @Test
+    public void testSelect_Invokersize2AndselectNotNull() throws Exception {
+        invokers.clear();
+        invokers.add(invoker1);
+        invokers.add(invoker2);
+        {
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker1);
+            Invoker invoker = cluster.select(null,null,invokers,selectedInvokers);
+            Assert.assertEquals(invoker2, invoker);
+        }
+        {
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker2);
+            Invoker invoker = cluster.select(null,null,invokers,selectedInvokers);
+            Assert.assertEquals(invoker1, invoker);
+        }
+    }
+    
+    @Test
+    public void testSelect_multiInvokers() throws Exception {
+        testSelect_multiInvokers( RoundRobinLoadBalance.NAME);
+        testSelect_multiInvokers( LeastActiveLoadBalance.NAME);
+        testSelect_multiInvokers( RandomLoadBalance.NAME);
+    }
+    
+    @Test
+    public void testCloseAvailablecheck(){
+        LoadBalance lb = EasyMock.createMock(LoadBalance.class);
+        EasyMock.expect(lb.select(invokers, invocation)).andReturn(invoker1);
+        EasyMock.replay(lb);
+        initlistsize5();
+        
+        Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+        Assert.assertEquals(false,sinvoker.isAvailable());
+        Assert.assertEquals(invoker1,sinvoker);
+        
+    }
+    
+    @Test
+    public void testDonotSelectAgainAndNoCheckAvailable(){
+        
+        LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME);
+        initlistsize5();
+        {
+            //边界测试.
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker2);
+            selectedInvokers.add(invoker3);
+            selectedInvokers.add(invoker4);
+            selectedInvokers.add(invoker5);
+            Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertSame(invoker1, sinvoker);
+        }
+        {
+            //边界测试.
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker1);
+            selectedInvokers.add(invoker3);
+            selectedInvokers.add(invoker4);
+            selectedInvokers.add(invoker5);
+            Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertSame(invoker2, sinvoker);
+        }
+        {
+            //边界测试.
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker1);
+            selectedInvokers.add(invoker2);
+            selectedInvokers.add(invoker4);
+            selectedInvokers.add(invoker5);
+            Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers );
+            Assert.assertSame(invoker3, sinvoker);
+        }
+        {
+            //边界测试.
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker1);
+            selectedInvokers.add(invoker2);
+            selectedInvokers.add(invoker3);
+            selectedInvokers.add(invoker4);
+            Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers );
+            Assert.assertSame(invoker5, sinvoker);
+        }
+        {
+            //边界测试.
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker1);
+            selectedInvokers.add(invoker2);
+            selectedInvokers.add(invoker3);
+            selectedInvokers.add(invoker4);
+            selectedInvokers.add(invoker5);
+            Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertTrue(invokers.contains(sinvoker));
+        }
+        
+    }
+    
+    @Test
+    public void testSelectAgainAndCheckAvailable(){
+        
+        LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME);
+        initlistsize5();
+        {
+            //边界测试.
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker1);
+            selectedInvokers.add(invoker2);
+            selectedInvokers.add(invoker3);
+            selectedInvokers.add(invoker5);
+            Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertTrue(sinvoker == invoker4 );
+        }
+        {
+            //边界测试.
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker2);
+            selectedInvokers.add(invoker3);
+            selectedInvokers.add(invoker4);
+            selectedInvokers.add(invoker5);
+            Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+        }
+        {
+            //边界测试.
+            for(int i=0;i<100;i++){
+                selectedInvokers.clear();
+                Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+                Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+            }
+        }
+        {
+            //边界测试.
+            for(int i=0;i<100;i++){
+                selectedInvokers.clear();
+                selectedInvokers.add(invoker1);
+                selectedInvokers.add(invoker3);
+                selectedInvokers.add(invoker5);
+                Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+                Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+            }
+        }
+        {
+            //边界测试.
+            for(int i=0;i<100;i++){
+                selectedInvokers.clear();
+                selectedInvokers.add(invoker1);
+                selectedInvokers.add(invoker3);
+                selectedInvokers.add(invoker2);
+                selectedInvokers.add(invoker4);
+                selectedInvokers.add(invoker5);
+                Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+                Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+            }
+        }
+    }
+    
+    
+    public void testSelect_multiInvokers(String lbname) throws Exception {
+        
+        int min=1000,max=5000;
+        Double d =  (Math.random()*(max-min+1)+min);
+        int runs =  d.intValue();
+        Assert.assertTrue(runs>min);
+        LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(lbname);
+        initlistsize5();
+        for(int i=0;i<runs;i++){
+            Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertEquals(true,sinvoker.isAvailable());
+        }
+        for(int i=0;i<runs;i++){
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker1);
+            Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertEquals(true,sinvoker.isAvailable());
+        }
+        for(int i=0;i<runs;i++){
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker2);
+            Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertEquals(true,sinvoker.isAvailable());
+        }
+        for(int i=0;i<runs;i++){
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker2);
+            selectedInvokers.add(invoker4);
+            Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertEquals(true,sinvoker.isAvailable());
+        }
+        for(int i=0;i<runs;i++){
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker1);
+            selectedInvokers.add(invoker3);
+            selectedInvokers.add(invoker5);
+            Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertEquals(true,sinvoker.isAvailable());
+        }
+        for(int i=0;i<runs;i++){
+            selectedInvokers.clear();
+            selectedInvokers.add(invoker1);
+            selectedInvokers.add(invoker2);
+            selectedInvokers.add(invoker3);
+            Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+            Assert.assertEquals(true,sinvoker.isAvailable());
+        }
+    }
+
+    /**
+     * 测试均衡.
+     */
+    @Test
+    public void testSelectBalance(){
+        
+        LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME);
+        initlistsize5();
+        
+        Map<Invoker,AtomicLong> counter = new ConcurrentHashMap<Invoker,AtomicLong>();
+        for(Invoker invoker :invokers){
+            counter.put(invoker, new AtomicLong(0));
+        }
+        int runs = 1000;
+        for(int i=0;i<runs;i++){
+            selectedInvokers.clear();
+            Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+            counter.get(sinvoker).incrementAndGet();
+        }
+        
+        for (Invoker minvoker :counter.keySet() ){
+            Long count = counter.get(minvoker).get();
+//            System.out.println(count);
+            if(minvoker.isAvailable())
+                Assert.assertTrue("count should > avg", count>runs/invokers.size());
+        }
+        
+        Assert.assertEquals(runs, counter.get(invoker2).get()+counter.get(invoker4).get());;
+        
+    }
+    
+    private void initlistsize5(){
+        invokers.clear();
+        selectedInvokers.clear();//需要清除,之前的测试中会主动将正确的invoker2放入其中.
+        invokers.add(invoker1);
+        invokers.add(invoker2);
+        invokers.add(invoker3);
+        invokers.add(invoker4);
+        invokers.add(invoker5);

+    }
+    @Test()

+    public void testTimeoutExceptionCode() {

+        List<Invoker<DemoService>> invokers = new ArrayList<Invoker<DemoService>>();

+        invokers.add(new Invoker<DemoService>() {

+

+            public Class<DemoService> getInterface() {

+                return DemoService.class;

+            }

+

+            public URL getUrl() {

+                return URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/" + DemoService.class.getName());

+            }

+

+            public boolean isAvailable() {

+                return false;

+            }

+

+            public Result invoke(Invocation invocation) throws RpcException {

+                throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "test timeout");

+            }

+

+            public void destroy() {

+            }

+        });

+        Directory<DemoService> directory = new StaticDirectory<DemoService>(invokers);

+        FailoverClusterInvoker<DemoService> failoverClusterInvoker = new FailoverClusterInvoker<DemoService>(directory);

+        try {

+            failoverClusterInvoker.invoke(new RpcInvocation("sayHello", new Class<?>[0], new Object[0]));

+            Assert.fail();

+        } catch (RpcException e) {

+            Assert.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode());

+        }

+        ForkingClusterInvoker<DemoService> forkingClusterInvoker = new ForkingClusterInvoker<DemoService>(directory);

+        try {

+            forkingClusterInvoker.invoke(new RpcInvocation("sayHello", new Class<?>[0], new Object[0]));

+            Assert.fail();

+        } catch (RpcException e) {

+            Assert.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode());

+        }

+        FailfastClusterInvoker<DemoService> failfastClusterInvoker = new FailfastClusterInvoker<DemoService>(directory);

+        try {

+            failfastClusterInvoker.invoke(new RpcInvocation("sayHello", new Class<?>[0], new Object[0]));

+            Assert.fail();

+        } catch (RpcException e) {

+            Assert.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode());

+        }

+    }

+	/**

+	 * 测试mock invoker选择是否正常

+	 */

+	@Test

+	public void testMockedInvokerSelect() {

+		initlistsize5();

+		invokers.add(mockedInvoker1);

+		

+		RpcInvocation mockedInvocation = new RpcInvocation();

+		mockedInvocation.setMethodName("sayHello");

+		mockedInvocation.setAttachment(Constants.INVOCATION_NEED_MOCK, "true");

+		List<Invoker<IHelloService>> mockedInvokers = dic.list(mockedInvocation);

+		Assert.assertEquals(1, mockedInvokers.size());

+		

+		List<Invoker<IHelloService>> invokers = dic.list(invocation);

+		Assert.assertEquals(5, invokers.size());

+	}

+	

+	public static interface IHelloService{}

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java
new file mode 100644
index 0000000..c5f9f0f
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java
@@ -0,0 +1,133 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import static org.junit.Assert.assertEquals;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import junit.framework.Assert;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.LogUtil;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.filter.DemoService;

+

+/**

+ * FailfastClusterInvokerTest

+ * @author tony.chenl

+ *

+ */

+@SuppressWarnings("unchecked")

+public class FailSafeClusterInvokerTest {

+    List<Invoker<DemoService>> invokers = new ArrayList<Invoker<DemoService>>();

+    URL url = URL.valueOf("test://test:11/test");

+    Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);

+    Invocation invocation;

+    Directory<DemoService> dic ;

+    Result result = new RpcResult();

+    /**

+     * @throws java.lang.Exception

+     */

+    

+    @Before

+    public void setUp() throws Exception {

+        

+        dic = EasyMock.createMock(Directory.class);

+        invocation = EasyMock.createMock(Invocation.class);

+        

+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();

+        EasyMock.expect(dic.getInterface()).andReturn(DemoService.class).anyTimes();

+        

+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();

+        EasyMock.replay(dic,invocation);

+        

+        invokers.add(invoker);

+    }

+

+    @After

+    public void tearDown(){

+        EasyMock.verify(invoker,dic,invocation);

+        

+    }

+    private void resetInvokerToException(){

+        EasyMock.reset(invoker);

+        EasyMock.expect(invoker.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.replay(invoker);

+    }

+    private void resetInvokerToNoException(){

+        EasyMock.reset(invoker);

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.replay(invoker);

+    }

+    

+    //TODO assert error log

+    @Test

+    public void testInvokeExceptoin() {

+        resetInvokerToException();

+        FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);

+        invoker.invoke(invocation);

+        Assert.assertNull(RpcContext.getContext().getInvoker());

+    }

+    

+    @Test()

+    public void testInvokeNoExceptoin() {

+        

+        resetInvokerToNoException();

+        

+        FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);

+        Result ret = invoker.invoke(invocation);

+        Assert.assertSame(result, ret);

+    }

+    

+    @Test()

+    public void testNoInvoke() {

+        dic = EasyMock.createMock(Directory.class);

+        invocation = EasyMock.createMock(Invocation.class);

+        

+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();

+        EasyMock.expect(dic.getInterface()).andReturn(DemoService.class).anyTimes();

+        

+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();

+        EasyMock.replay(dic,invocation);

+        

+        resetInvokerToNoException();

+        

+        FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);

+        LogUtil.start();

+        invoker.invoke(invocation);

+        assertEquals(1,LogUtil.findMessage("No provider"));

+        LogUtil.stop();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java
new file mode 100644
index 0000000..5d558c8
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java
@@ -0,0 +1,152 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import static org.junit.Assert.assertEquals;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import junit.framework.Assert;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.LogUtil;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+

+/**

+ * FailbackClusterInvokerTest

+ * 

+ * @author tony.chenl

+ */

+@SuppressWarnings("unchecked")

+public class FailbackClusterInvokerTest {

+

+    List<Invoker<FailbackClusterInvokerTest>> invokers = new ArrayList<Invoker<FailbackClusterInvokerTest>>();

+    URL                                       url      = URL.valueOf("test://test:11/test");

+    Invoker<FailbackClusterInvokerTest>       invoker  = EasyMock.createMock(Invoker.class);

+    Invocation                                invocation;

+    Directory<FailbackClusterInvokerTest>     dic;

+    Result                                    result   = new RpcResult();

+

+    /**

+     * @throws java.lang.Exception

+     */

+

+    @Before

+    public void setUp() throws Exception {

+

+        dic = EasyMock.createMock(Directory.class);

+        invocation = EasyMock.createMock(Invocation.class);

+

+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();

+        EasyMock.expect(dic.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();

+

+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();

+        EasyMock.replay(dic, invocation);

+

+        invokers.add(invoker);

+    }

+

+    @After

+    public void tearDown() {

+        EasyMock.verify(invoker, dic, invocation);

+

+    }

+

+    private void resetInvokerToException() {

+        EasyMock.reset(invoker);

+        EasyMock.expect(invoker.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();

+        EasyMock.replay(invoker);

+    }

+

+    private void resetInvokerToNoException() {

+        EasyMock.reset(invoker);

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();

+        EasyMock.replay(invoker);

+    }

+

+    @Test

+    public void testInvokeExceptoin() {

+        resetInvokerToException();

+        FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(

+                                                                                                                            dic);

+        invoker.invoke(invocation);

+        Assert.assertNull(RpcContext.getContext().getInvoker());

+    }

+

+    @Test()

+    public void testInvokeNoExceptoin() {

+

+        resetInvokerToNoException();

+

+        FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(

+                                                                                                                            dic);

+        Result ret = invoker.invoke(invocation);

+        Assert.assertSame(result, ret);

+    }

+

+    @Test()

+    public void testNoInvoke() {

+        dic = EasyMock.createMock(Directory.class);

+        invocation = EasyMock.createMock(Invocation.class);

+

+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();

+        EasyMock.expect(dic.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();

+

+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();

+        EasyMock.replay(dic, invocation);

+

+        invokers.add(invoker);

+

+        resetInvokerToNoException();

+

+        FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(

+                                                                                                                            dic);

+        LogUtil.start();

+        invoker.invoke(invocation);

+        assertEquals(1, LogUtil.findMessage("Failback to invoke"));

+        LogUtil.stop();

+    }

+

+    @Test()

+    public void testRetryFailed() {

+

+        resetInvokerToException();

+

+        FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(

+                                                                                                                            dic);

+        invoker.invoke(invocation);

+        Assert.assertNull(RpcContext.getContext().getInvoker());

+        invoker.retryFailed();// when retry the invoker which get from failed map already is not the mocked invoker,so

+                              // it can be invoke successfully

+    }

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java
new file mode 100644
index 0000000..93f4b3f
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java
@@ -0,0 +1,136 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;
+
+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.fail;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import junit.framework.Assert;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+
+/**
+ * FailfastClusterInvokerTest
+ * @author liuchao
+ *
+ */
+@SuppressWarnings("unchecked")
+public class FailfastClusterInvokerTest {
+    List<Invoker<FailfastClusterInvokerTest>> invokers = new ArrayList<Invoker<FailfastClusterInvokerTest>>();
+    URL url = URL.valueOf("test://test:11/test");
+    Invoker<FailfastClusterInvokerTest> invoker1 = EasyMock.createMock(Invoker.class);
+    Invocation invocation;
+    Directory<FailfastClusterInvokerTest> dic ;
+    Result result = new RpcResult();
+    /**
+     * @throws java.lang.Exception
+     */
+    
+    @Before
+    public void setUp() throws Exception {
+        
+        dic = EasyMock.createMock(Directory.class);
+        invocation = EasyMock.createMock(Invocation.class);
+        
+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+        EasyMock.expect(dic.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();
+        
+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+        EasyMock.replay(dic,invocation);
+        
+        invokers.add(invoker1);
+    }
+
+    @After
+    public void tearDown(){
+        EasyMock.verify(invoker1,dic,invocation);
+        
+    }
+    private void resetInvoker1ToException(){
+        EasyMock.reset(invoker1);
+        EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();
+        EasyMock.replay(invoker1);
+    }
+    private void resetInvoker1ToNoException(){
+        EasyMock.reset(invoker1);
+        EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();
+        EasyMock.replay(invoker1);
+    }
+    
+    @Test(expected = RpcException.class)
+    public void testInvokeExceptoin() {
+        resetInvoker1ToException();
+        FailfastClusterInvoker<FailfastClusterInvokerTest> invoker = new FailfastClusterInvoker<FailfastClusterInvokerTest>(dic);
+        invoker.invoke(invocation);
+        Assert.assertSame(invoker1, RpcContext.getContext().getInvoker());
+    }
+    
+    @Test()
+    public void testInvokeNoExceptoin() {
+        
+        resetInvoker1ToNoException();
+        
+        FailfastClusterInvoker<FailfastClusterInvokerTest> invoker = new FailfastClusterInvoker<FailfastClusterInvokerTest>(dic);
+        Result ret = invoker.invoke(invocation);
+        Assert.assertSame(result, ret);
+    }

+    

+    @Test()

+    public void testNoInvoke() {

+        dic = EasyMock.createMock(Directory.class);

+        invocation = EasyMock.createMock(Invocation.class);

+        

+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();

+        EasyMock.expect(dic.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();

+        

+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();

+        EasyMock.replay(dic,invocation);

+        

+        invokers.add(invoker1);

+        

+        resetInvoker1ToNoException();

+        

+        FailfastClusterInvoker<FailfastClusterInvokerTest> invoker = new FailfastClusterInvoker<FailfastClusterInvokerTest>(dic);

+        try {

+            invoker.invoke(invocation);

+            fail();

+        } catch (RpcException expected) {

+            assertFalse(expected.getCause() instanceof RpcException);

+        }

+    }

+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java
new file mode 100644
index 0000000..1d5442d
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java
@@ -0,0 +1,178 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;
+
+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertSame;

+import static org.junit.Assert.assertTrue;

+import static org.junit.Assert.fail;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.easymock.EasyMock;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+
+/**
+ * FailoverClusterInvokerTest
+ * @author liuchao
+ *
+ */
+@SuppressWarnings("unchecked")
+public class FailoverClusterInvokerTest {
+    List<Invoker<FailoverClusterInvokerTest>> invokers = new ArrayList<Invoker<FailoverClusterInvokerTest>>();
+    int retries = 5;
+    URL url = URL.valueOf("test://test:11/test?retries="+retries);
+    Invoker<FailoverClusterInvokerTest> invoker1 = EasyMock.createMock(Invoker.class);
+    Invoker<FailoverClusterInvokerTest> invoker2 = EasyMock.createMock(Invoker.class);
+    Invocation invocation;
+    Directory<FailoverClusterInvokerTest> dic ;
+    Result result = new RpcResult();
+    /**
+     * @throws java.lang.Exception
+     */
+    
+    @Before
+    public void setUp() throws Exception {
+        
+        dic = EasyMock.createMock(Directory.class);
+        invocation = EasyMock.createMock(Invocation.class);
+        
+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+        EasyMock.expect(dic.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+        
+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+        EasyMock.replay(dic,invocation);
+        
+        invokers.add(invoker1);
+        invokers.add(invoker2);
+    }
+
+    
+    @Test
+    public void testInvokeWithRuntimeException() {
+        EasyMock.reset(invoker1);
+        EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+        EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+        EasyMock.replay(invoker1);
+        
+        EasyMock.reset(invoker2);
+        EasyMock.expect(invoker2.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+        EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker2.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+        EasyMock.replay(invoker2);
+        
+        FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
+        try {

+            invoker.invoke(invocation);

+            fail();

+        } catch (RpcException expected) {

+            assertEquals(0,expected.getCode());

+            assertFalse(expected.getCause() instanceof RpcException);

+        }
+    }
+    
+    @Test()
+    public void testInvokeWithRPCException() {
+        
+        EasyMock.reset(invoker1);
+        EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RpcException()).anyTimes();
+        EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+        EasyMock.replay(invoker1);
+        
+        EasyMock.reset(invoker2);
+        EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+        EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker2.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+        EasyMock.replay(invoker2);
+        
+        FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
+        for(int i=0;i<100;i++){
+            Result ret = invoker.invoke(invocation);
+            assertSame(result, ret);
+        }
+    }
+    
+    @Test()
+    public void testInvoke_retryTimes() {
+        
+        EasyMock.reset(invoker1);
+        EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RpcException(RpcException.TIMEOUT_EXCEPTION)).anyTimes();
+        EasyMock.expect(invoker1.isAvailable()).andReturn(false).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+        EasyMock.replay(invoker1);
+        
+        EasyMock.reset(invoker2);
+        EasyMock.expect(invoker2.invoke(invocation)).andThrow(new RpcException()).anyTimes();
+        EasyMock.expect(invoker2.isAvailable()).andReturn(false).anyTimes();
+        EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+        EasyMock.expect(invoker2.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+        EasyMock.replay(invoker2);
+        
+        FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
+        try{
+            Result ret = invoker.invoke(invocation);
+            assertSame(result, ret);

+            fail();
+        }catch (RpcException expected) {

+            assertTrue(expected.isTimeout());
+            assertTrue(expected.getMessage().indexOf((retries+1)+" times")>0);
+        }
+    }

+    

+    @Test()

+    public void testNoInvoke() {

+        dic = EasyMock.createMock(Directory.class);

+        invocation = EasyMock.createMock(Invocation.class);

+        

+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();

+        EasyMock.expect(dic.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();

+        

+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();

+        EasyMock.replay(dic,invocation);

+        

+        invokers.add(invoker1);

+        

+        

+        FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);

+        try {

+            invoker.invoke(invocation);

+            fail();

+        } catch (RpcException expected) {

+            assertFalse(expected.getCause() instanceof RpcException);

+        }

+    }

+    
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java
new file mode 100644
index 0000000..347ecbf
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java
@@ -0,0 +1,152 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.cluster.support;

+

+import static org.junit.Assert.assertFalse;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import junit.framework.Assert;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+

+/**

+ * ForkingClusterInvokerTest

+ * 

+ * @author tony.chenl

+ */

+@SuppressWarnings("unchecked")

+public class ForkingClusterInvokerTest {

+

+    List<Invoker<ForkingClusterInvokerTest>> invokers = new ArrayList<Invoker<ForkingClusterInvokerTest>>();

+    URL                                      url      = URL.valueOf("test://test:11/test?forks=2");

+    Invoker<ForkingClusterInvokerTest>       invoker1 = EasyMock.createMock(Invoker.class);

+    Invoker<ForkingClusterInvokerTest>       invoker2 = EasyMock.createMock(Invoker.class);

+    Invoker<ForkingClusterInvokerTest>       invoker3 = EasyMock.createMock(Invoker.class);

+    Invocation                               invocation;

+    Directory<ForkingClusterInvokerTest>     dic;

+    Result                                   result   = new RpcResult();

+

+    /**

+     * @throws java.lang.Exception

+     */

+

+    @Before

+    public void setUp() throws Exception {

+

+        dic = EasyMock.createMock(Directory.class);

+        invocation = EasyMock.createMock(Invocation.class);

+

+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();

+        EasyMock.expect(dic.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();

+

+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();

+        EasyMock.replay(dic, invocation);

+

+        invokers.add(invoker1);

+        invokers.add(invoker2);

+        invokers.add(invoker3);

+

+    }

+

+    @After

+    public void tearDown() {

+        EasyMock.verify(invoker1, dic, invocation);

+

+    }

+

+    private void resetInvokerToException() {

+        EasyMock.reset(invoker1);

+        EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();

+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker1.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();

+        EasyMock.replay(invoker1);

+        EasyMock.reset(invoker2);

+        EasyMock.expect(invoker2.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();

+        EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker2.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();

+        EasyMock.replay(invoker2);

+        EasyMock.reset(invoker3);

+        EasyMock.expect(invoker3.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();

+        EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker3.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();

+        EasyMock.replay(invoker3);

+    }

+

+    private void resetInvokerToNoException() {

+        EasyMock.reset(invoker1);

+        EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();

+        EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker1.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();

+        EasyMock.replay(invoker1);

+        EasyMock.reset(invoker2);

+        EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();

+        EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker2.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();

+        EasyMock.replay(invoker2);

+        EasyMock.reset(invoker3);

+        EasyMock.expect(invoker3.invoke(invocation)).andReturn(result).anyTimes();

+        EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();

+        EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker3.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();

+        EasyMock.replay(invoker3);

+    }

+

+    @Test

+    public void testInvokeExceptoin() {

+        resetInvokerToException();

+        ForkingClusterInvoker<ForkingClusterInvokerTest> invoker = new ForkingClusterInvoker<ForkingClusterInvokerTest>(

+                                                                                     dic);

+        

+        try {

+            invoker.invoke(invocation);

+            Assert.fail();

+        } catch (RpcException expected) {

+            Assert.assertTrue(expected.getMessage().contains("Failed to forking invoke provider"));

+            assertFalse(expected.getCause() instanceof RpcException);

+        }

+    }

+

+    @Test()

+    public void testInvokeNoExceptoin() {

+

+        resetInvokerToNoException();

+

+        ForkingClusterInvoker<ForkingClusterInvokerTest> invoker = new ForkingClusterInvoker<ForkingClusterInvokerTest>(

+                                                                                                                        dic);

+        Result ret = invoker.invoke(invocation);

+        Assert.assertSame(result, ret);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/Menu.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/Menu.java
new file mode 100644
index 0000000..5788008
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/Menu.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class Menu {
+    
+    private Map<String, List<String>> menus = new HashMap<String, List<String>>();
+    
+    public Menu() {}
+    
+    public Menu( Map<String, List<String>> menus ) {
+        this.menus.putAll( menus );
+    }
+    
+    public void putMenuItem( String menu, String item ) {
+        List<String> items = menus.get( menu );
+        if ( item == null ) {
+            items = new ArrayList<String>();
+            menus.put( menu, items );
+        }
+        items.add( item );
+    }
+    
+    public void addMenu( String menu, List<String> items ) {
+        List<String> menuItems = menus.get( menu );
+        if ( menuItems == null ) {
+            menus.put( menu, new ArrayList<String>( items ) );
+        } else {
+            menuItems.addAll( new ArrayList<String>( items ) );
+        }
+    }
+    
+    public Map<String, List<String>> getMenus() {
+        return Collections.unmodifiableMap( menus );
+    }
+    
+    public void merge( Menu menu ) {
+        for( Map.Entry<String, List<String>> entry : menu.menus.entrySet() ) {
+            addMenu( entry.getKey(), entry.getValue() );
+        }
+    }
+
+}
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MenuService.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MenuService.java
new file mode 100644
index 0000000..40368ca
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MenuService.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public interface MenuService {
+    
+    public Menu getMenu();
+    
+    public void addMenu( String menu, List<String> items );
+
+}
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java
new file mode 100644
index 0000000..97f4619
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MergeableClusterInvokerTest {
+
+    private Directory directory = EasyMock.createMock( Directory.class );
+    private Invoker firstInvoker = EasyMock.createMock( Invoker.class );
+    private Invoker secondInvoker = EasyMock.createMock( Invoker.class );
+    private Invocation invocation = EasyMock.createMock( Invocation.class );
+
+    private MergeableClusterInvoker<MenuService> mergeableClusterInvoker;
+    
+    private Map<String, List<String>> firstMenuMap = new HashMap<String, List<String>>() {
+        {
+            put( "1", new ArrayList<String>() {
+                {
+                    add( "10" );
+                    add( "11" );
+                    add( "12" );
+                }
+            } );
+            put( "2", new ArrayList<String>() {
+
+                {
+                    add( "20" );
+                    add( "21" );
+                    add( "22" );
+                }
+            } );
+        }
+    };
+    private Map<String, List<String>> secondMenuMap = new HashMap<String, List<String>>() {
+        {
+            put( "2", new ArrayList<String>() {
+
+                {
+                    add( "23" );
+                    add( "24" );
+                    add( "25" );
+                }
+            } );
+            put( "3", new ArrayList<String>() {
+
+                {
+                    add( "30" );
+                    add( "31" );
+                    add( "32" );
+                }
+            } );
+        }
+    };
+    
+    private Menu firstMenu = new Menu( firstMenuMap );
+    private Menu secondMenu = new Menu( secondMenuMap );
+    
+    private URL url = URL.valueOf( new StringBuilder( 32 )
+                                           .append( "test://test/" )
+                                           .append( MenuService.class.getName() ).toString() );
+    
+    @Before
+    public void setUp() throws Exception {
+
+        directory = EasyMock.createMock( Directory.class );
+        firstInvoker = EasyMock.createMock( Invoker.class );
+        secondInvoker = EasyMock.createMock( Invoker.class );
+        invocation = EasyMock.createMock( Invocation.class );
+
+    }
+
+    @Test
+    public void testGetMenuSuccessfully() throws Exception {
+
+        // setup
+        url = url.addParameter( Constants.MERGER_KEY, "merge" );
+
+        EasyMock.expect( invocation.getMethodName() ).andReturn( "getMenu" ).anyTimes();
+        EasyMock.expect( invocation.getParameterTypes() ).andReturn( new Class<?>[]{ } ).anyTimes();
+        EasyMock.expect( invocation.getArguments() ).andReturn( new Object[]{ } ).anyTimes();
+        EasyMock.expect( invocation.getAttachments() ).andReturn( new HashMap<String, String>() )
+                .anyTimes();
+        EasyMock.expect( invocation.getInvoker() ).andReturn( firstInvoker ).anyTimes();
+        EasyMock.replay( invocation );
+
+        EasyMock.expect( firstInvoker.getUrl() ).andReturn(
+                url.addParameter( Constants.GROUP_KEY, "first" ) ).anyTimes();
+        EasyMock.expect( firstInvoker.getInterface() ).andReturn( MenuService.class ).anyTimes();
+        EasyMock.expect( firstInvoker.invoke( invocation ) ).andReturn(
+                new RpcResult( firstMenu ) ).anyTimes();
+        EasyMock.replay( firstInvoker );
+
+        EasyMock.expect( secondInvoker.getUrl() ).andReturn(
+                url.addParameter( Constants.GROUP_KEY, "second" ) ).anyTimes();
+        EasyMock.expect( secondInvoker.getInterface() ).andReturn( MenuService.class ).anyTimes();
+        EasyMock.expect( secondInvoker.invoke( invocation ) ).andReturn(
+                new RpcResult( secondMenu ) ).anyTimes();
+        EasyMock.replay( secondInvoker );
+
+        EasyMock.expect( directory.list( invocation ) ).andReturn( new ArrayList() {
+
+            {
+                add( firstInvoker );
+                add( secondInvoker );
+            }
+        } ).anyTimes();
+        EasyMock.expect( directory.getUrl() ).andReturn( url ).anyTimes();
+        EasyMock.expect( directory.getInterface() ).andReturn( MenuService.class ).anyTimes();
+        EasyMock.replay( directory );
+
+        mergeableClusterInvoker = new MergeableClusterInvoker<MenuService>( directory );
+
+        // FIXME
+        // invoke
+        /*Result result = mergeableClusterInvoker.invoke( invocation );
+        Assert.assertTrue( result.getResult() instanceof Menu );
+        Menu menu = ( Menu ) result.getResult();
+        Map<String, List<String>> expected = new HashMap<String, List<String>>();
+        merge( expected, firstMenuMap );
+        merge( expected, secondMenuMap );
+        Assert.assertEquals( expected, menu.getMenus() );*/
+
+    }
+
+    @Test
+    public void testAddMenu() throws Exception {
+
+        String menu = "first";
+        List<String> menuItems = new ArrayList<String>(){
+            {
+                add( "1" );
+                add( "2" );
+            }
+        };
+        
+        EasyMock.expect( invocation.getMethodName() ).andReturn( "addMenu" ).anyTimes();
+        EasyMock.expect( invocation.getParameterTypes() ).andReturn(
+                new Class<?>[]{ String.class, List.class } ).anyTimes();
+        EasyMock.expect( invocation.getArguments() ).andReturn( new Object[]{ menu, menuItems } )
+                .anyTimes();
+        EasyMock.expect( invocation.getAttachments() ).andReturn( new HashMap<String, String>() )
+                .anyTimes();
+        EasyMock.expect( invocation.getInvoker() ).andReturn( firstInvoker ).anyTimes();
+        EasyMock.replay( invocation );
+
+        EasyMock.expect( firstInvoker.getUrl() ).andReturn(
+                url.addParameter( Constants.GROUP_KEY, "first" ) ).anyTimes();
+        EasyMock.expect( firstInvoker.getInterface() ).andReturn( MenuService.class ).anyTimes();
+        EasyMock.expect( firstInvoker.invoke( invocation ) ).andReturn( new RpcResult() )
+                .anyTimes();
+        EasyMock.replay( firstInvoker );
+
+        EasyMock.expect( secondInvoker.getUrl() ).andReturn(
+                url.addParameter( Constants.GROUP_KEY, "second" ) ).anyTimes();
+        EasyMock.expect( secondInvoker.getInterface() ).andReturn( MenuService.class ).anyTimes();
+        EasyMock.expect( secondInvoker.invoke( invocation ) ).andReturn(new RpcResult() )
+                .anyTimes();
+        EasyMock.replay( secondInvoker );
+
+        EasyMock.expect( directory.list( invocation ) ).andReturn( new ArrayList() {
+
+            {
+                add( firstInvoker );
+                add( secondInvoker );
+            }
+        } ).anyTimes();
+        EasyMock.expect( directory.getUrl() ).andReturn( url ).anyTimes();
+        EasyMock.expect( directory.getInterface() ).andReturn( MenuService.class ).anyTimes();
+        EasyMock.replay( directory );
+
+        mergeableClusterInvoker = new MergeableClusterInvoker<MenuService>( directory );
+        
+        // FIXME
+        //Result result = mergeableClusterInvoker.invoke( invocation );
+        //Assert.assertNull( result.getResult() );
+
+    }
+
+    static void merge( Map<String, List<String>> first, Map<String, List<String>> second ) {
+        for( Map.Entry<String, List<String>> entry : second.entrySet() ) {
+            List<String> value = first.get( entry.getKey() );
+            if ( value != null ) {
+                value.addAll( entry.getValue() );
+            } else {
+                first.put( entry.getKey(), entry.getValue() );
+            }
+        }
+    }
+
+}
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
new file mode 100644
index 0000000..3cb7a02
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
@@ -0,0 +1,753 @@
+package com.alibaba.dubbo.rpc.cluster.support.wrapper;

+

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+

+import junit.framework.Assert;

+

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.cluster.Directory;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;

+import com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;

+import com.alibaba.dubbo.rpc.support.MockProtocol;

+

+public class MockClusterInvokerTest {

+	

+	List<Invoker<IHelloService>> invokers = new ArrayList<Invoker<IHelloService>>();

+	

+	@Before

+	public void beforeMethod(){

+		invokers.clear();

+	}

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerInvoke_normal(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName());

+		url = url.addParameter(Constants.MOCK_KEY, "fail" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+        URL mockUrl = URL.valueOf("mock://localhost/"+IHelloService.class.getName()

+				+"?getSomething.mock=return aa");

+		

+		Protocol protocol = new MockProtocol();

+		Invoker<IHelloService> mInvoker1 = protocol.refer(IHelloService.class, mockUrl);

+		invokers.add(mInvoker1);

+        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("something", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("sayHello");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals(null, ret.getValue());

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerInvoke_failmock(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter(Constants.MOCK_KEY, "fail:return null" )

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+        URL mockUrl = URL.valueOf("mock://localhost/"+IHelloService.class.getName()

+				+"?getSomething.mock=return aa").addParameters(url.getParameters());

+		

+		Protocol protocol = new MockProtocol();

+		Invoker<IHelloService> mInvoker1 = protocol.refer(IHelloService.class, mockUrl);

+		invokers.add(mInvoker1);

+        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("aa", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething2");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals(null, ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("sayHello");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals(null, ret.getValue());

+	}

+	

+	

+	/**

+	 * 测试mock策略是否正常-force-mork

+	 */

+	@Test

+	public void testMockInvokerInvoke_forcemock(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName());

+		url = url.addParameter(Constants.MOCK_KEY, "force:return null" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+	    URL mockUrl = URL.valueOf("mock://localhost/"+IHelloService.class.getName()

+				+"?getSomething.mock=return aa&getSomething3xx.mock=return xx")

+				.addParameters(url.getParameters());

+		

+		Protocol protocol = new MockProtocol();

+		Invoker<IHelloService> mInvoker1 = protocol.refer(IHelloService.class, mockUrl);

+		invokers.add(mInvoker1);

+	    

+		//方法配置了mock

+	    RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+	    Result ret = cluster.invoke(invocation);

+	    Assert.assertEquals("aa", ret.getValue());

+	    

+	  //如果没有配置mock,则直接返回null

+	    invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething2");

+	    ret = cluster.invoke(invocation);

+	    Assert.assertEquals(null, ret.getValue());

+	    

+	    //如果没有配置mock,则直接返回null

+	    invocation = new RpcInvocation();

+		invocation.setMethodName("sayHello");

+	    ret = cluster.invoke(invocation);

+	    Assert.assertEquals(null, ret.getValue());

+	}

+	

+	@Test

+	public void testMockInvokerInvoke_forcemock_defaultreturn(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName());

+		url = url.addParameter(Constants.MOCK_KEY, "force" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+	    URL mockUrl = URL.valueOf("mock://localhost/"+IHelloService.class.getName()

+				+"?getSomething.mock=return aa&getSomething3xx.mock=return xx&sayHello.mock=return ")

+				.addParameters(url.getParameters());

+		

+		Protocol protocol = new MockProtocol();

+		Invoker<IHelloService> mInvoker1 = protocol.refer(IHelloService.class, mockUrl);

+		invokers.add(mInvoker1);

+	    

+	    RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("sayHello");

+	    Result ret = cluster.invoke(invocation);

+	    Assert.assertEquals(null, ret.getValue());

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_Fock_someMethods(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getSomething.mock","fail:return x")

+				.addParameter("getSomething2.mock","force:return y");

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("something", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething2");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("y", ret.getValue());

+        

+      //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething3");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("something3", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("sayHello");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals(null, ret.getValue());

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_Fock_WithOutDefault(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getSomething.mock","fail:return x")

+				.addParameter("getSomething2.mock","force:return y")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("x", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething2");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("y", ret.getValue());

+        

+      //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething3");

+		try {

+			ret = cluster.invoke(invocation);

+			Assert.fail();

+		}catch (RpcException e) {

+			

+		}

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_Fock_WithDefault(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("mock","fail:return null")

+				.addParameter("getSomething.mock","fail:return x")

+				.addParameter("getSomething2.mock","force:return y")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("x", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething2");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("y", ret.getValue());

+        

+      //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething3");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals(null, ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("sayHello");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals(null, ret.getValue());

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_Fock_WithFailDefault(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("mock","fail:return z")

+				.addParameter("getSomething.mock","fail:return x")

+				.addParameter("getSomething2.mock","force:return y")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("x", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething2");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("y", ret.getValue());

+        

+      //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething3");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("z", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("sayHello");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("z", ret.getValue());

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_Fock_WithForceDefault(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("mock","force:return z")

+				.addParameter("getSomething.mock","fail:return x")

+				.addParameter("getSomething2.mock","force:return y")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("x", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething2");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("y", ret.getValue());

+        

+      //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething3");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("z", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("sayHello");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("z", ret.getValue());

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_Fock_Default(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("mock","fail:return x")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("x", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething2");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("x", ret.getValue());

+        

+      //如d

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("sayHello");

+        ret = cluster.invoke(invocation);

+        Assert.assertEquals("x", ret.getValue());

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_checkCompatible_return(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getSomething.mock","return x")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("x", ret.getValue());

+        

+        //如果没有配置mock,则直接返回null

+        invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething3");

+		try{

+			ret = cluster.invoke(invocation);

+			Assert.fail("fail invoke");

+		}catch(RpcException e){

+			

+		}

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("mock","true")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("somethingmock", ret.getValue());

+	}

+	

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock2(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("mock","fail")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("somethingmock", ret.getValue());

+	}

+	/**

+	 * 测试mock策略是否正常-fail-mock

+	 */

+	@Test

+	public void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock3(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("mock","force");

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals("somethingmock", ret.getValue());

+	}

+	

+	@Test

+	public void testMockInvokerFromOverride_Invoke_check_String(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getSomething.mock","force:return 1688")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getSomething");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertTrue("result type must be String but was : " + ret.getValue().getClass(), ret.getValue() instanceof String);

+        Assert.assertEquals("1688", (String)ret.getValue());

+	}

+	

+	@Test

+	public void testMockInvokerFromOverride_Invoke_check_int(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getInt1.mock","force:return 1688")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getInt1");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertTrue("result type must be integer but was : " + ret.getValue().getClass(), ret.getValue() instanceof Integer);

+        Assert.assertEquals(new Integer(1688), (Integer)ret.getValue());

+	}

+	

+	@Test

+	public void testMockInvokerFromOverride_Invoke_check_boolean(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getBoolean1.mock","force:return true")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getBoolean1");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertTrue("result type must be Boolean but was : " + ret.getValue().getClass(), ret.getValue() instanceof Boolean);

+        Assert.assertEquals(true, Boolean.parseBoolean(ret.getValue().toString()));

+	}

+	

+	@Test

+	public void testMockInvokerFromOverride_Invoke_check_Boolean(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getBoolean2.mock","force:return true")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getBoolean2");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals(true, Boolean.parseBoolean(ret.getValue().toString()));

+	}

+	

+	@SuppressWarnings("unchecked")

+	@Test

+	public void testMockInvokerFromOverride_Invoke_check_ListString_empty(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getListString.mock","force:return empty")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getListString");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals(0, ((List<String>)ret.getValue()).size());

+	}

+	

+	@SuppressWarnings("unchecked")

+	@Test

+	public void testMockInvokerFromOverride_Invoke_check_ListString(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getListString.mock","force:return [\"hi\",\"hi2\"]")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getListString");

+        Result ret = cluster.invoke(invocation);

+        List<String> rl = (List<String>)ret.getValue();

+        Assert.assertEquals(2, rl.size());

+        Assert.assertEquals("hi", rl.get(0));

+	}

+	

+	@SuppressWarnings("unchecked")

+	@Test

+	public void testMockInvokerFromOverride_Invoke_check_ListPojo_empty(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getUsers.mock","force:return empty")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getUsers");

+        Result ret = cluster.invoke(invocation);

+        Assert.assertEquals(0, ((List<User>)ret.getValue()).size());

+	}

+	

+	@SuppressWarnings("unchecked")

+	@Test

+	public void testMockInvokerFromOverride_Invoke_check_ListPojo(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getUsers.mock","force:return [{id:1, name:\"hi1\"}, {id:2, name:\"hi2\"}]")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getUsers");

+        Result ret = cluster.invoke(invocation);

+        List<User> rl = (List<User>)ret.getValue();

+        System.out.println(rl);

+        Assert.assertEquals(2, rl.size());

+        Assert.assertEquals("hi1", ((User)rl.get(0)).getName());

+	}

+	

+	@Test

+	public void testMockInvokerFromOverride_Invoke_check_ListPojo_error(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getUsers.mock","force:return [{id:x, name:\"hi1\"}]")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getUsers");

+		try{

+			cluster.invoke(invocation);

+		}catch (RpcException e) {

+		}

+	}

+	

+	@Test

+	public void testMockInvokerFromOverride_Invoke_force_throw(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getBoolean2.mock","force:throw ")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getBoolean2");

+		try {

+			cluster.invoke(invocation);

+			Assert.fail();

+		} catch (RpcException e) {

+			Assert.assertFalse("not custem exception", e.isBiz());

+		}

+	}

+	

+	@Test

+	public void testMockInvokerFromOverride_Invoke_force_throwCustemException() throws Throwable{

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getBoolean2.mock","force:throw com.alibaba.dubbo.rpc.cluster.support.wrapper.MyMockException")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getBoolean2");

+		try {

+			cluster.invoke(invocation).recreate();

+			Assert.fail();

+		} catch (MyMockException e) {

+			

+		}

+	}

+	

+	@Test

+	public void testMockInvokerFromOverride_Invoke_force_throwCustemExceptionNotFound(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("getBoolean2.mock","force:throw java.lang.RuntimeException2")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getBoolean2");

+		try {

+			cluster.invoke(invocation);

+			Assert.fail();

+		} catch (Exception e) {

+			Assert.assertTrue(e.getCause() instanceof IllegalStateException);

+		}

+	}

+	

+	@Test

+	public void testMockInvokerFromOverride_Invoke_mock_false(){

+		URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())

+				.addParameter("mock","false")

+				.addParameter("invoke_return_error", "true" );

+		Invoker<IHelloService> cluster = getClusterInvoker(url);        

+		//方法配置了mock

+        RpcInvocation invocation = new RpcInvocation();

+		invocation.setMethodName("getBoolean2");

+		try {

+			cluster.invoke(invocation);

+			Assert.fail();

+		} catch (RpcException e) {

+			Assert.assertTrue(e.isTimeout());

+		}

+	}

+	

+	@SuppressWarnings({ "unchecked", "rawtypes" })

+	private Invoker<IHelloService> getClusterInvoker(URL url){

+		//javasssit方式对方法参数类型判断严格,如果invocation数据设置不全,调用会失败.

+		final URL durl = url.addParameter("proxy", "jdk");

+		invokers.clear();

+		ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("jdk");

+		Invoker<IHelloService> invoker1 = proxy.getInvoker(new HelloService(), IHelloService.class, durl);

+		invokers.add(invoker1);

+		

+		Directory<IHelloService> dic = new StaticDirectory<IHelloService>(durl, invokers, null);

+		AbstractClusterInvoker<IHelloService> cluster = new AbstractClusterInvoker(dic) {

+            @Override

+            protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance)

+                    throws RpcException {

+            	if (durl.getParameter("invoke_return_error", false)){

+            		throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "test rpc exception");

+            	} else {

+            		return ((Invoker<?>)invokers.get(0)).invoke(invocation);

+            	}

+            }

+        };

+        return new MockClusterInvoker<IHelloService>(dic, cluster);

+	}

+	

+	public static interface IHelloService{

+		String getSomething();

+		String getSomething2();

+		String getSomething3();

+		int getInt1();

+		boolean getBoolean1();

+		Boolean getBoolean2();

+		public List<String> getListString();

+		public List<User> getUsers();

+		void sayHello();

+	}

+	public static class HelloService implements IHelloService {

+		public String getSomething() {

+			return "something";

+		}

+		public String getSomething2() {

+			return "something2";

+		}

+		public String getSomething3() {

+			return "something3";

+		}

+		public int getInt1() {

+			return 1;

+		}

+		public boolean getBoolean1() {

+			return false;

+		}

+		public Boolean getBoolean2() {

+			return Boolean.FALSE;

+		}

+		public List<String> getListString() {

+			return Arrays.asList(new String[]{"Tom","Jerry"});

+		}

+		public List<User> getUsers() {

+			return Arrays.asList(new User[]{new User(1,"Tom"),new User(2,"Jerry")});

+		}

+		public void sayHello() {

+			System.out.println("hello prety");

+		}

+	}

+	

+	public static class IHelloServiceMock implements IHelloService {

+		public IHelloServiceMock() {

+			

+		}

+		public String getSomething() {

+			return "somethingmock";

+		}

+		public String getSomething2() {

+			return "something2mock";

+		}

+		public String getSomething3() {

+			return "something3mock";

+		}

+		public List<String> getListString() {

+			return Arrays.asList(new String[]{"Tommock","Jerrymock"});

+		}

+		public List<User> getUsers() {

+			return Arrays.asList(new User[]{new User(1,"Tommock"),new User(2,"Jerrymock")});

+		}

+		public int getInt1() {

+			return 1;

+		}

+		public boolean getBoolean1() {

+			return false;

+		}

+		public Boolean getBoolean2() {

+			return Boolean.FALSE;

+		}

+		

+		public void sayHello() {

+			System.out.println("hello prety");

+		}

+	}

+	

+	public static class User{

+		private int id;

+		private String name;

+		

+		public User() {

+		}

+		public User(int id, String name) {

+			super();

+			this.id = id;

+			this.name = name;

+		}

+		public int getId() {

+			return id;

+		}

+		public String getName() {

+			return name;

+		}

+		public void setId(int id) {

+			this.id = id;

+		}

+		public void setName(String name) {

+			this.name = name;

+		}

+		

+	}

+}

diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MyMockException.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MyMockException.java
new file mode 100644
index 0000000..e2aa147
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MyMockException.java
@@ -0,0 +1,12 @@
+package com.alibaba.dubbo.rpc.cluster.support.wrapper;

+

+

+public class MyMockException extends RuntimeException{

+	

+	public MyMockException(String message) {

+		super(message);

+	}

+

+	private static final long serialVersionUID = 2851692379597990457L;

+

+}

diff --git a/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript
new file mode 100644
index 0000000..c76de32
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript
@@ -0,0 +1,11 @@
+function route(invokers,invocation,context){ 
+	var result = new java.util.ArrayList(invokers.size());
+	
+	for (i=0;i<invokers.size(); i++){ 
+	    if (invokers.get(i).isAvailable()) {
+	        result.add(invokers.get(i)) ;
+	    }
+	} ; 
+	return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/methodrule.javascript b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/methodrule.javascript
new file mode 100644
index 0000000..77ff456
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/methodrule.javascript
@@ -0,0 +1,10 @@
+function route(invokers,invocation,context){ 
+	var result = new java.util.ArrayList();
+	if (invokers.size()>1 && invocation.getMethodName() .equals("method1")) {
+	   	result.add(invokers.get(0)) ;
+	} else {
+		result.add(invokers.get(1)) ;
+	}
+	return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript
new file mode 100644
index 0000000..1fcdcae
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript
@@ -0,0 +1,11 @@
+function route(invokers,invocation,context){ 
+	var result = new java.util.ArrayList(invokers.size());
+	
+	for (i=0;i<invokers.size(); i++){ 
+	    if (!invokers.get(i).isAvailable()) {
+	        result.add(invokers.get(i)) ;
+	    }
+	} ; 
+	return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/log4j.xml b/dubbo-cluster/src/test/resources/log4j.xml
new file mode 100644
index 0000000..e2c0263
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+	<appender name="DUBBO" class="com.alibaba.dubbo.common.utils.DubboAppender">
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />
+		</layout>
+	</appender>
+	<root>
+		<level value="INFO" />
+		<appender-ref ref="DUBBO" />
+	</root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml
new file mode 100644
index 0000000..087500f
--- /dev/null
+++ b/dubbo-common/pom.xml
@@ -0,0 +1,50 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-common</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Common Module</name>
+	<description>The common module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.javassist</groupId>
+			<artifactId>javassist</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>hessian-lite</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+			<scope>provided</scope>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java
new file mode 100644
index 0000000..5e9ef13
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java
@@ -0,0 +1,491 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common;

+

+import java.util.regex.Pattern;

+

+/**

+ * Constants

+ * 

+ * @author william.liangf

+ */

+public class Constants {

+

+    public static final String  PROVIDER                           = "provider";

+

+    public static final String  CONSUMER                           = "consumer";

+

+    public static final String  PROVIDERS                          = "providers";

+

+    public static final String  CONSUMERS                          = "consumers";

+

+    public static final String  REGISTER                           = "register";

+

+    public static final String  UNREGISTER                         = "unregister";

+

+    public static final String  SUBSCRIBE                          = "subscribe";

+

+    public static final String  UNSUBSCRIBE                        = "unsubscribe";

+

+    public static final String  VALIDATION_KEY                     = "validation";

+

+    public static final String  CACHE_KEY                          = "cache";

+

+    public static final String  DUBBO_PROPERTIES_KEY               = "dubbo.properties.file";

+

+    public static final String  DEFAULT_DUBBO_PROPERTIES           = "dubbo.properties";

+

+    public static final String  SENT_KEY                           = "sent";

+

+    public static final boolean DEFAULT_SENT                       = false;

+

+    public static final String  REGISTRY_PROTOCOL                  = "registry";

+

+    public static final String  $INVOKE                            = "$invoke";

+

+    public static final String  $ECHO                              = "$echo";

+

+    public static final int     DEFAULT_IO_THREADS                 = Runtime.getRuntime()

+                                                                           .availableProcessors() + 1;

+

+    public static final String  DEFAULT_PROXY                      = "javassist";

+

+    public static final int     DEFAULT_PAYLOAD                    = 8 * 1024 * 1024;                      // 8M

+

+    public static final String  DEFAULT_CLUSTER                    = "failover";

+

+    public static final String  DEFAULT_DIRECTORY                  = "dubbo";

+

+    public static final String  DEFAULT_LOADBALANCE                = "random";

+

+    public static final String  DEFAULT_PROTOCOL                   = "dubbo";

+

+    public static final String  DEFAULT_EXCHANGER                  = "header";

+

+    public static final String  DEFAULT_TRANSPORTER                = "netty";

+

+    public static final String  DEFAULT_REMOTING_SERVER            = "netty";

+

+    public static final String  DEFAULT_REMOTING_CLIENT            = "netty";

+

+    public static final String  DEFAULT_REMOTING_CODEC             = "dubbo";

+

+    public static final String  DEFAULT_REMOTING_SERIALIZATION     = "hessian2";

+

+    public static final String  DEFAULT_HTTP_SERVER                = "servlet";

+

+    public static final String  DEFAULT_HTTP_CLIENT                = "jdk";

+

+    public static final String  DEFAULT_HTTP_SERIALIZATION         = "json";

+

+    public static final String  DEFAULT_CHARSET                    = "UTF-8";

+

+    public static final int     DEFAULT_WEIGHT                     = 100;

+

+    public static final int     DEFAULT_FORKS                      = 2;

+

+    public static final String  DEFAULT_THREAD_NAME                = "Dubbo";

+

+    public static final int     DEFAULT_THREADS                    = 200;

+

+    public static final int     DEFAULT_QUEUES                     = 0;

+

+    public static final int     DEFAULT_THREAD_ALIVE               = 60 * 1000;

+

+    public static final int     DEFAULT_CONNECTIONS                = 0;

+

+    public static final int     DEFAULT_ACCEPTS                    = 0;

+

+    public static final int     DEFAULT_IDLE_TIMEOUT               = 600 * 1000;

+

+    public static final int     DEFAULT_HEARTBEAT                  = 0;

+

+    public static final int     DEFAULT_TIMEOUT                    = 5000;

+

+    public static final int     DEFAULT_RETRIES                    = 2;

+

+    // default buffer size is 8k.

+    public static final int     DEFAULT_BUFFER_SIZE                = 8 * 1024;

+

+    public static final int     MAX_BUFFER_SIZE                    = 16 * 1024;

+

+    public static final int     MIN_BUFFER_SIZE                    = 1 * 1024;

+

+    public static final String  REMOVE_VALUE_PREFIX                = "-";

+

+    public static final String  HIDE_KEY_PREFIX                    = ".";

+

+    public static final String  DEFAULT_KEY_PREFIX                 = "default.";

+

+    public static final String  DEFAULT_KEY                        = "default";

+

+    public static final String  LOADBALANCE_KEY                    = "loadbalance";

+

+    // key for router type, for e.g., "script"/"file",  corresponding to ScriptRouterFactory.NAME, FileRouterFactory.NAME 

+    public static final String  ROUTER_KEY                         = "router";

+

+    public static final String  CLUSTER_KEY                        = "cluster";

+

+    public static final String  REGISTRY_KEY                       = "registry";

+

+    public static final String  MONITOR_KEY                        = "monitor";

+

+    public static final String  DEFAULT_REGISTRY                   = "dubbo";

+

+    public static final String  BACKUP_KEY                         = "backup";

+

+    public static final String  DIRECTORY_KEY                      = "directory";

+

+    public static final String  DEPRECATED_KEY                     = "deprecated";

+

+    public static final String  ANYHOST_KEY                        = "anyhost";

+

+    public static final String  ANYHOST                            = "0.0.0.0";

+

+    public static final String  APPLICATION_KEY                    = "application";

+

+    public static final String  LOCAL_KEY                          = "local";

+

+    public static final String  STUB_KEY                           = "stub";

+

+    public static final String  MOCK_KEY                           = "mock";

+

+    public static final String  PROTOCOL_KEY                       = "protocol";

+

+    public static final String  PROXY_KEY                          = "proxy";

+

+    public static final String  WEIGHT_KEY                         = "weight";

+

+    public static final String  FORKS_KEY                          = "forks";

+

+    public static final String  DEFAULT_THREADPOOL                 = "fixed";

+

+    public static final String  DEFAULT_CLIENT_THREADPOOL          = "cached";

+

+    public static final String  THREADPOOL_KEY                     = "threadpool";

+

+    public static final String  THREAD_NAME_KEY                    = "threadname";

+

+    public static final String  IO_THREADS_KEY                     = "iothreads";

+

+    public static final String  THREADS_KEY                        = "threads";

+

+    public static final String  QUEUES_KEY                         = "queues";

+

+    public static final String  THREAD_ALIVE_KEY                   = "threadalive";

+

+    public static final String  EXECUTES_KEY                       = "executes";

+

+    public static final String  BUFFER_KEY                         = "buffer";

+

+    public static final String  PAYLOAD_KEY                        = "payload";

+

+    public static final String  REFERENCE_FILTER_KEY               = "reference.filter";

+

+    public static final String  INVOKER_LISTENER_KEY               = "invoker.listener";

+

+    public static final String  SERVICE_FILTER_KEY                 = "service.filter";

+

+    public static final String  EXPORTER_LISTENER_KEY              = "exporter.listener";

+

+    public static final String  ACCESS_LOG_KEY                     = "accesslog";

+

+    public static final String  ACTIVES_KEY                        = "actives";

+

+    public static final String  CONNECTIONS_KEY                    = "connections";

+

+    public static final String  ACCEPTS_KEY                        = "accepts";

+

+    public static final String  IDLE_TIMEOUT_KEY                   = "idle.timeout";

+

+    public static final String  HEARTBEAT_KEY                      = "heartbeat";

+

+    public static final String  HEARTBEAT_TIMEOUT_KEY              = "heartbeat.timeout";

+

+    public static final String  CONNECT_TIMEOUT_KEY                = "connect.timeout";

+

+    public static final String  TIMEOUT_KEY                        = "timeout";

+

+    public static final String  RETRIES_KEY                        = "retries";

+

+    public static final String  PROMPT_KEY                         = "prompt";

+

+    public static final String  DEFAULT_PROMPT                     = "dubbo>";

+

+    public static final String  CODEC_KEY                          = "codec";

+    public static final String  DOWNSTREAM_CODEC_KEY               = "codec.downstream";

+

+    public static final String  SERIALIZATION_KEY                  = "serialization";

+

+    public static final String  EXCHANGER_KEY                      = "exchanger";

+

+    public static final String  TRANSPORTER_KEY                    = "transporter";

+

+    public static final String  SERVER_KEY                         = "server";

+

+    public static final String  CLIENT_KEY                         = "client";

+

+    public static final String  ASYNC_KEY                          = "async";

+

+    public static final String  TOKEN_KEY                          = "token";

+

+    public static final String  METHODS_KEY                        = "methods";

+

+    public static final String  CHARSET_KEY                        = "charset";

+

+    public static final String  RECONNECT_KEY                      = "reconnect";

+

+    public static final String  SEND_RECONNECT_KEY                 = "send.reconnect";

+

+    public static final int     DEFAULT_RECONNECT_PERIOD           = 2000;

+

+    public static final String  SHUTDOWN_TIMEOUT_KEY               = "shutdown.timeout";

+

+    public static final int     DEFAULT_SHUTDOWN_TIMEOUT           = 1000 * 60 * 5;

+

+    public static final String  TIMESTAMP_KEY                      = "timestamp";

+

+    public static final String  CHECK_KEY                          = "check";

+

+    public static final String  REGISTER_KEY                       = "register";

+

+    public static final String  GROUP_KEY                          = "group";

+

+    public static final String  PATH_KEY                           = "path";

+

+    public static final String  INTERFACE_KEY                      = "interface";

+

+    public static final String  GENERIC_KEY                        = "generic";

+

+    public static final String  FILE_KEY                           = "file";

+

+    public static final String  WAIT_KEY                           = "wait";

+

+    public static final String  ADMIN_KEY                          = "admin";

+

+    public static final String  VERSION_KEY                        = "version";

+

+    public static final String  REVISION_KEY                       = "revision";

+

+    public static final String  DUBBO_VERSION_KEY                  = "dubbo";

+

+    public static final String  HESSIAN_VERSION_KEY                = "hessian.version";

+

+    public static final String  DISPATHER_KEY                      = "dispather";

+

+    public static final String  CHANNEL_HANDLER_KEY                = "channel.handler";

+

+    public static final String  DEFAULT_CHANNEL_HANDLER            = "default";

+

+    public static final String  ANY_VALUE                          = "*";

+

+    public static final String  COMMA_SEPARATOR                    = ",";

+

+    public static final Pattern COMMA_SPLIT_PATTERN                = Pattern

+                                                                           .compile("\\s*[,]+\\s*");

+

+    public final static String  PATH_SEPARATOR                     = "/";

+

+    public static final String  REGISTRY_SEPARATOR                 = "|";

+

+    public static final Pattern REGISTRY_SPLIT_PATTERN             = Pattern

+                                                                           .compile("\\s*[|]+\\s*");

+

+    public static final String  SEMICOLON_SEPARATOR                = ";";

+

+    public static final Pattern SEMICOLON_SPLIT_PATTERN            = Pattern

+                                                                           .compile("\\s*[;]+\\s*");

+

+    public static final String  CONNECT_QUEUE_CAPACITY             = "connect.queue.capacity";

+

+    public static final String  CONNECT_QUEUE_WARNING_SIZE         = "connect.queue.warning.size";

+

+    public static final int     DEFAULT_CONNECT_QUEUE_WARNING_SIZE = 1000;

+

+    public static final String  CHANNEL_ATTRIBUTE_READONLY_KEY     = "channel.readonly";

+

+    public static final String  CHANNEL_READONLYEVENT_SENT_KEY     = "channel.readonly.sent";

+

+    public static final String  CHANNEL_SEND_READONLYEVENT_KEY     = "channel.readonly.send";

+

+    public static final String  SUBSCRIBE_PROTOCOL                 = "subscribe";

+

+    public static final String  EMPTY_PROTOCOL                     = "empty";

+

+    public static final String  ROUTE_PROTOCOL                     = "route";

+

+    public static final String  MOCK_PROTOCOL                      = "mock";

+

+    public static final String  RETURN_PREFIX                      = "return ";

+    public static final String  THROW_PREFIX                       = "throw";

+

+    public static final String  FAIL_PREFIX                        = "fail:";

+

+    public static final String  FORCE_PREFIX                       = "force:";

+

+    public static final String  MERGER_KEY                         = "merger";

+

+    public static final String  TPS_MAX_KEY                        = "tps.max";

+

+    /**

+     * 集群时是否排除非available的invoker

+     */

+    public static final String  CLUSTER_AVAILABLE_CHECK_KEY        = "cluster.availablecheck";

+

+    /**

+     */

+    public static final boolean DEFAULT_CLUSTER_AVAILABLE_CHECK    = true;

+

+    /**

+     * 集群时是否启用sticky策略

+     */

+    public static final String  CLUSTER_STICKY_KEY                 = "sticky";

+

+    /**

+     * sticky默认值.

+     */

+    public static final boolean DEFAULT_CLUSTER_STICKY             = false;

+

+    /**

+     * 创建client时,是否先要建立连接。

+     */

+    public static final String  LAZY_CONNECT_KEY                   = "lazy";

+

+    /**

+     * lazy连接的初始状态是连接状态还是非连接状态?

+     */

+    public static final String  LAZY_CONNECT_INITIAL_STATE_KEY     = "connect.lazy.initial.state";

+

+    /**

+     * lazy连接的初始状态默认是连接状态.

+     */

+    public static final boolean DEFAULT_LAZY_CONNECT_INITIAL_STATE = true;

+

+    /**

+     * 注册中心是否同步存储文件,默认异步

+     */

+    public static final String  REGISTRY_FILESAVE_SYNC_KEY         = "save.file";

+

+    /**

+     * 注册中心失败事件重试事件

+     */

+    public static final String  REGISTRY_RETRY_PERIOD_KEY          = "retry.period";

+

+    /**

+     * 重试周期

+     */

+    public static final int DEFAULT_REGISTRY_RETRY_PERIOD          =  5 * 1000;

+    

+    /**

+     * 注册中心自动重连时间

+     */

+    public static final String  REGISTRY_RECONNECT_PERIOD_KEY      = "reconnect.period";

+

+    public static final int     DEFAULT_REGISTRY_RECONNECT_PERIOD  = 3 * 1000;

+    

+    public static final String  SESSION_TIMEOUT_KEY                = "session";

+

+    public static final int     DEFAULT_SESSION_TIMEOUT            = 60 * 1000;

+

+    /**

+     * 注册中心导出URL参数的KEY

+     */

+    public static final String  EXPORT_KEY                         = "export";

+

+    /**

+     * 注册中心引用URL参数的KEY

+     */

+    public static final String  REFER_KEY                          = "refer";

+

+    /**

+     * callback inst id

+     */

+    public static final String  CALLBACK_SERVICE_KEY               = "callback.service.instid";

+

+    /**

+     * 每个客户端同一个接口 callback服务实例的限制

+     */

+    public static final String  CALLBACK_INSTANCES_LIMIT_KEY       = "callbacks";

+

+    /**

+     * 每个客户端同一个接口 callback服务实例的限制

+     */

+    public static final int     DEFAULT_CALLBACK_INSTANCES         = 1;

+

+    public static final String  CALLBACK_SERVICE_PROXY_KEY         = "callback.service.proxy";

+

+    public static final String  IS_CALLBACK_SERVICE                = "is_callback_service";

+

+    /**

+     * channel中callback的invokers

+     */

+    public static final String  CHANNEL_CALLBACK_KEY               = "channel.callback.invokers.key";

+

+    @Deprecated

+    public static final String  SHUTDOWN_WAIT_SECONDS_KEY          = "dubbo.service.shutdown.wait.seconds";

+

+    public static final String  SHUTDOWN_WAIT_KEY                  = "dubbo.service.shutdown.wait";

+

+    public static final String  IS_SERVER_KEY                      = "isserver";

+

+    /**

+     * 默认值毫秒,避免重新计算.

+     */

+    public static final int     DEFAULT_SERVER_SHUTDOWN_TIMEOUT    = 10000;

+

+    public static final String  ON_CONNECT_KEY                     = "onconnect";

+

+    public static final String  ON_DISCONNECT_KEY                  = "ondisconnect";

+

+    public static final String  RETURN_KEY                         = "return";

+

+    public static final String  ON_INVOKE_METHOD_KEY               = "oninvoke.method";

+

+    public static final String  ON_RETURN_METHOD_KEY               = "onreturn.method";

+

+    public static final String  ON_THROW_METHOD_KEY                = "onthrow.method";

+

+    public static final String  ON_INVOKE_INSTANCE_KEY             = "oninvoke.instance";

+

+    public static final String  ON_RETURN_INSTANCE_KEY             = "onreturn.instance";

+

+    public static final String  ON_THROW_INSTANCE_KEY              = "onthrow.instance";

+

+    public static final String  OVERRIDE_PROTOCOL                  = "override";

+

+    public static final String  RULE_KEY                           = "rule";

+

+    public static final String  TYPE_KEY                           = "type";

+

+    // when ROUTER_KEY's value is set to ROUTER_TYPE_CLEAR, RegistryDirectory will clean all current routers

+    public static final String  ROUTER_TYPE_CLEAR                  = "clean";

+

+    public static final String  DEFAULT_SCRIPT_TYPE_KEY            = "javascript";

+

+    public static final String  STUB_EVENT_KEY                     = "dubbo.stub.event";

+

+    public static final boolean DEFAULT_STUB_EVENT                 = false;

+

+    public static final String  STUB_EVENT_METHODS_KEY             = "dubbo.stub.event.methods";

+

+    //invocation attachment属性中如果有此值,则选择mock invoker

+    public static final String  INVOCATION_NEED_MOCK               = "invocation.need.mock";

+

+    /*

+     * private Constants(){ }

+     */

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Extension.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Extension.java
new file mode 100644
index 0000000..c48b796
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Extension.java
@@ -0,0 +1,42 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+/**

+ * 请使用KV格式的配置文件:META-INF/dubbo/com.xxx.Protocol,文件内容:xxx=com.xxx.XxxProtocol

+ * @deprecated

+ * @author william.liangf

+ * @author ding.lid

+ */

+@Deprecated

+@Documented

+@Retention(RetentionPolicy.RUNTIME)

+@Target({ElementType.TYPE})

+public @interface Extension {

+

+    /**

+     * @deprecated

+     */

+    @Deprecated

+	String value() default "";

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Node.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Node.java
new file mode 100644
index 0000000..8884158
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Node.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common;

+

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * Node. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public interface Node {

+

+    /**

+     * get url.

+     * 

+     * @return url.

+     */

+    URL getUrl();

+    

+    /**

+     * is available.

+     * 

+     * @return available.

+     */

+    boolean isAvailable();

+

+    /**

+     * destroy.

+     */

+    void destroy();

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.java
new file mode 100644
index 0000000..ba907f0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.java
@@ -0,0 +1,263 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common;

+

+import java.io.UnsupportedEncodingException;

+import java.net.URLDecoder;

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.StringUtils;

+

+/**

+ * 兼容2.0.5之前版本

+ * @deprecated

+ * @author tony.chenl

+ */

+@Deprecated

+public class Parameters {

+        private final Map<String, String> parameters;

+        

+        protected static final Logger logger = LoggerFactory.getLogger(Parameters.class);

+        

+        public Parameters(String... pairs) {

+            this(toMap(pairs));

+        }

+        

+        public Parameters(Map<String, String> parameters){

+            this.parameters = Collections.unmodifiableMap(parameters != null ? new HashMap<String, String>(parameters) : new HashMap<String, String>(0));

+        }

+        

+        private static Map<String, String> toMap(String... pairs) {

+            Map<String, String> parameters = new HashMap<String, String>();

+            if (pairs.length > 0) {

+                if (pairs.length % 2 != 0) {

+                    throw new IllegalArgumentException("pairs must be even.");

+                }

+                for (int i = 0; i < pairs.length; i = i + 2) {

+                    parameters.put(pairs[i], pairs[i + 1]);

+                }

+            }

+            return parameters;

+        }

+

+        public Map<String, String> getParameters() {

+            return parameters;

+        }

+        

+        public <T> T getExtension(Class<T> type, String key) {

+            String name = getParameter(key);

+            return ExtensionLoader.getExtensionLoader(type).getExtension(name);

+        }

+

+        public <T> T getExtension(Class<T> type, String key, String defaultValue) {

+            String name = getParameter(key, defaultValue);

+            return ExtensionLoader.getExtensionLoader(type).getExtension(name);

+        }

+        

+        public <T> T getMethodExtension(Class<T> type, String method, String key) {

+            String name = getMethodParameter(method, key);

+            return ExtensionLoader.getExtensionLoader(type).getExtension(name);

+        }

+

+        public <T> T getMethodExtension(Class<T> type, String method, String key, String defaultValue) {

+            String name = getMethodParameter(method, key, defaultValue);

+            return ExtensionLoader.getExtensionLoader(type).getExtension(name);

+        }

+        

+        public String getDecodedParameter(String key) {

+            return getDecodedParameter(key, null);

+        }

+        

+        public String getDecodedParameter(String key, String defaultValue) {

+            String value = getParameter(key, defaultValue);

+            if (value != null && value.length() > 0) { 

+                try {

+                    value = URLDecoder.decode(value, "UTF-8");

+                } catch (UnsupportedEncodingException e) {

+                    logger.error(e.getMessage(), e);

+                }

+            }

+            return value;

+        }

+

+        public String getParameter(String key) {

+            String value = parameters.get(key);

+            if (value == null || value.length() == 0) {

+                value = parameters.get(Constants.HIDE_KEY_PREFIX + key);

+            }

+            if (value == null || value.length() == 0) {

+                value = parameters.get(Constants.DEFAULT_KEY_PREFIX + key);

+            }

+            if (value == null || value.length() == 0) {

+                value = parameters.get(Constants.HIDE_KEY_PREFIX + Constants.DEFAULT_KEY_PREFIX + key);

+            }

+            return value;

+        }

+

+        public String getParameter(String key, String defaultValue) {

+            String value = getParameter(key);

+            if (value == null || value.length() == 0) {

+                return defaultValue;

+            }

+            return value;

+        }

+

+        public int getIntParameter(String key) {

+            String value = getParameter(key);

+            if (value == null || value.length() == 0) {

+                return 0;

+            }

+            return Integer.parseInt(value);

+        }

+

+        public int getIntParameter(String key, int defaultValue) {

+            String value = getParameter(key);

+            if (value == null || value.length() == 0) {

+                return defaultValue;

+            }

+            return Integer.parseInt(value);

+        }

+        

+        public int getPositiveIntParameter(String key, int defaultValue) {

+            if (defaultValue <= 0) {

+                throw new IllegalArgumentException("defaultValue <= 0");

+            }

+            String value = getParameter(key);

+            if (value == null || value.length() == 0) {

+                return defaultValue;

+            }

+            int i = Integer.parseInt(value);

+            if (i > 0) {

+                return i;

+            }

+            return defaultValue;

+        }

+

+        public boolean getBooleanParameter(String key) {

+            String value = getParameter(key);

+            if (value == null || value.length() == 0) {

+                return false;

+            }

+            return Boolean.parseBoolean(value);

+        }

+

+        public boolean getBooleanParameter(String key, boolean defaultValue) {

+            String value = getParameter(key);

+            if (value == null || value.length() == 0) {

+                return defaultValue;

+            }

+            return Boolean.parseBoolean(value);

+        }

+

+        public boolean hasParamter(String key) {

+            String value = getParameter(key);

+            return value != null && value.length() > 0;

+        }

+

+        public String getMethodParameter(String method, String key) {

+            String value = parameters.get(method + "." + key);

+            if (value == null || value.length() == 0) {

+                value = parameters.get(Constants.HIDE_KEY_PREFIX + method + "." + key);

+            }

+            if (value == null || value.length() == 0) {

+                return getParameter(key);

+            }

+            return value;

+        }

+

+        public String getMethodParameter(String method, String key, String defaultValue) {

+            String value = getMethodParameter(method, key);

+            if (value == null || value.length() == 0) {

+                return defaultValue;

+            }

+            return value;

+        }

+

+        public int getMethodIntParameter(String method, String key) {

+            String value = getMethodParameter(method, key);

+            if (value == null || value.length() == 0) {

+                return 0;

+            }

+            return Integer.parseInt(value);

+        }

+

+        public int getMethodIntParameter(String method, String key, int defaultValue) {

+            String value = getMethodParameter(method, key);

+            if (value == null || value.length() == 0) {

+                return defaultValue;

+            }

+            return Integer.parseInt(value);

+        }

+

+        public int getMethodPositiveIntParameter(String method, String key, int defaultValue) {

+            if (defaultValue <= 0) {

+                throw new IllegalArgumentException("defaultValue <= 0");

+            }

+            String value = getMethodParameter(method, key);

+            if (value == null || value.length() == 0) {

+                return defaultValue;

+            }

+            int i = Integer.parseInt(value);

+            if (i > 0) {

+                return i;

+            }

+            return defaultValue;

+        }

+

+        public boolean getMethodBooleanParameter(String method, String key) {

+            String value = getMethodParameter(method, key);

+            if (value == null || value.length() == 0) {

+                return false;

+            }

+            return Boolean.parseBoolean(value);

+        }

+

+        public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) {

+            String value = getMethodParameter(method, key);

+            if (value == null || value.length() == 0) {

+                return defaultValue;

+            }

+            return Boolean.parseBoolean(value);

+        }

+

+        public boolean hasMethodParamter(String method, String key) {

+            String value = getMethodParameter(method, key);

+            return value != null && value.length() > 0;

+        }

+        

+        public static Parameters parseParameters(String query) {

+            return new Parameters(StringUtils.parseQueryString(query));

+        }

+        

+        public boolean equals(Object o) {

+            return parameters.equals(o);

+        }

+

+        public int hashCode() {

+            return parameters.hashCode();

+        }

+

+        @Override

+        public String toString() {

+            return StringUtils.toQueryString(getParameters());

+        }

+        

+    }
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Resetable.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Resetable.java
new file mode 100644
index 0000000..1ef0985
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Resetable.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common;

+

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * Resetable.

+ * 

+ * @author william.liangf

+ */

+public interface Resetable {

+

+    /**

+     * reset.

+     * 

+     * @param url

+     */

+    void reset(URL url);

+    

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java
new file mode 100644
index 0000000..f5ba217
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java
@@ -0,0 +1,1226 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common;

+

+import java.io.Serializable;

+import java.io.UnsupportedEncodingException;

+import java.net.InetSocketAddress;

+import java.net.MalformedURLException;

+import java.net.URLDecoder;

+import java.net.URLEncoder;

+import java.util.Arrays;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.TreeMap;

+import java.util.concurrent.ConcurrentHashMap;

+

+import com.alibaba.dubbo.common.utils.CollectionUtils;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+

+/**

+ * URL - Uniform Resource Locator (Immutable, ThreadSafe)

+ * <p>

+ * url example:

+ * <ul>

+ * <li>http://www.facebook.com/friends?param1=value1&amp;param2=value2

+ * <li>http://username:password@10.20.130.230:8080/list?version=1.0.0

+ * <li>ftp://username:password@192.168.1.7:21/1/read.txt

+ * <li>registry://192.168.1.7:9090/com.alibaba.service1?param1=value1&amp;param2=value2

+ * </ul>

+ * <p>

+ * Some strange example below:

+ * <ul>

+ * <li>192.168.1.3:20880<br>

+ * for this case, url protocol = null, url host = 192.168.1.3, port = 20880, url path = null

+ * <li>file:///home/user1/router.js?type=script<br>

+ * for this case, url protocol = null, url host = null, url path = home/user1/router.js

+ * <li>file://home/user1/router.js?type=script<br>

+ * for this case, url protocol = file, url host = home, url path = user1/router.js

+ * <li>file:///D:/1/router.js?type=script<br>

+ * for this case, url protocol = file, url host = null, url path = D:/1/router.js

+ * <li>file:/D:/1/router.js?type=script<br>

+ * same as above file:///D:/1/router.js?type=script 

+ * <li>/home/user1/router.js?type=script <br>

+ * for this case, url protocol = null, url host = null, url path = home/user1/router.js

+ * <li>home/user1/router.js?type=script <br>

+ * for this case, url protocol = null, url host = home, url path = user1/router.js

+ * </ul>

+ * 

+ * @author william.liangf

+ * @author ding.lid

+ * @see java.net.URL

+ * @see java.net.URI

+ */

+public final class URL implements Serializable {

+

+    private static final long serialVersionUID = -1985165475234910535L;

+

+    private final String protocol;

+

+	private final String username;

+

+	private final String password;

+

+	private final String host;

+

+	private final int port;

+

+	private final String path;

+

+    private final Map<String, String> parameters;

+    

+    // ==== cache ====

+    

+    private volatile transient Map<String, Number> numbers;

+

+    private volatile transient String ip;

+

+    private volatile transient String fullString;

+

+    private volatile transient String identityString;

+    

+    private volatile transient String parameterString;

+

+    private volatile transient String string;

+    

+    protected URL() {

+        this.protocol = null;

+        this.username = null;

+        this.password = null;

+        this.host = null;

+        this.port = 0;

+        this.path = null;

+        this.parameters = null;

+    }

+    

+	public URL(String protocol, String host, int port) {

+	    this(protocol, null, null, host, port, null, (Map<String, String>) null);

+	}

+	

+	public URL(String protocol, String host, int port, String... pairs) {

+        this(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs));

+    }

+	

+	public URL(String protocol, String host, int port, Map<String, String> parameters) {

+        this(protocol, null, null, host, port, null, parameters);

+    }

+	

+	public URL(String protocol, String host, int port, String path) {

+	    this(protocol, null, null, host, port, path, (Map<String, String>) null);

+	}

+

+	public URL(String protocol, String host, int port, String path, String... pairs) {

+        this(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs));

+    }

+	

+	public URL(String protocol, String host, int port, String path, Map<String, String> parameters) {

+		this(protocol, null, null, host, port, path, parameters);

+	}

+	

+	public URL(String protocol, String username, String password, String host, int port, String path) {

+        this(protocol, username, password, host, port, path, (Map<String, String>) null);

+    }

+	

+	public URL(String protocol, String username, String password, String host, int port, String path, String... pairs) {

+	    this(protocol, username, password, host, port, path, CollectionUtils.toStringMap(pairs));

+	}

+	

+	public URL(String protocol, String username, String password, String host, int port, String path, Map<String, String> parameters) {

+		if ((username == null || username.length() == 0) 

+				&& password != null && password.length() > 0) {

+			throw new IllegalArgumentException("Invalid url, password without username!");

+		}

+		this.protocol = protocol;

+		this.username = username;

+		this.password = password;

+		this.host = host != null && host.length() > 0 ? NetUtils.filterLocalHost(host) : host;

+		this.port = (port < 0 ? 0 : port);

+		this.path = path;

+		// trim the beginning "/"

+		while(path != null && path.startsWith("/")) {

+		    path = path.substring(1);

+		}

+		if (parameters == null) {

+		    parameters = new HashMap<String, String>();

+		} else {

+		    parameters = new HashMap<String, String>(parameters);

+		}

+		if (NetUtils.isAnyHost(host)) {

+		    parameters.put("anyhost", "true");

+		} else if (NetUtils.isLocalHost(host)) {

+            parameters.put("localhost", "true");

+        }

+		this.parameters = Collections.unmodifiableMap(parameters);

+	}

+

+    /**

+     * Parse url string

+     * 

+     * @param url URL string

+     * @return URL instance

+     * @see URL

+     */

+    public static URL valueOf(String url) {

+        if (url == null || (url = url.trim()).length() == 0) {

+            throw new IllegalArgumentException("url == null");

+        }

+        String protocol = null;

+        String username = null;

+        String password = null;

+        String host = null;

+        int port = 0;

+        String path = null;

+        Map<String, String> parameters = null;

+        int i = url.indexOf("?"); // seperator between body and parameters 

+        if (i >= 0) {

+            String[] parts = url.substring(i + 1).split("\\&");

+            parameters = new HashMap<String, String>();

+            for (String part : parts) {

+                part = part.trim();

+                if (part.length() > 0) {

+                    int j = part.indexOf('=');

+                    if (j >= 0) {

+                        parameters.put(part.substring(0, j), part.substring(j + 1));

+                    } else {

+                        parameters.put(part, part);

+                    }

+                }

+            }

+            url = url.substring(0, i);

+        }

+        i = url.indexOf("://");

+        if (i >= 0) {

+            if(i == 0) throw new IllegalStateException("url missing protocol: \"" + url + "\"");

+            protocol = url.substring(0, i);

+            url = url.substring(i + 3);

+        }

+        else {

+            // case: file:/path/to/file.txt

+            i = url.indexOf(":/");

+            if(i>=0) {

+                if(i == 0) throw new IllegalStateException("url missing protocol: \"" + url + "\"");

+                protocol = url.substring(0, i);

+                url = url.substring(i + 1);

+            }

+        }

+        

+        i = url.indexOf("/");

+        if (i >= 0) {

+            path = url.substring(i + 1);

+            url = url.substring(0, i);

+        }

+        i = url.indexOf("@");

+        if (i >= 0) {

+            username = url.substring(0, i);

+            int j = username.indexOf(":");

+            if (j >= 0) {

+                password = username.substring(j + 1);

+                username = username.substring(0, j);

+            }

+            url = url.substring(i + 1);

+        }

+        i = url.indexOf(":");

+        if (i >= 0 && i < url.length() - 1) {

+            port = Integer.parseInt(url.substring(i + 1));

+            url = url.substring(0, i);

+        }

+        if(url.length() > 0) host = url;

+        return new URL(protocol, username, password, host, port, path, parameters);

+    }

+

+	public String getProtocol() {

+		return protocol;

+	}

+

+	public String getUsername() {

+		return username;

+	}

+

+	public String getPassword() {

+		return password;

+	}

+

+	public String getHost() {

+		return host;

+	}

+	

+	/**

+	 * 获取IP地址.

+	 * 

+	 * 请注意:

+	 * 如果和Socket的地址对比,

+	 * 或用地址作为Map的Key查找,

+	 * 请使用IP而不是Host,

+	 * 否则配置域名会有问题

+	 * 

+	 * @return ip

+	 */

+	public String getIp() {

+	    if (ip == null) {

+	        ip = NetUtils.getIpByHost(host);

+	    }

+	    return ip;

+	}

+	

+	public int getPort() {

+		return port;

+	}

+

+	public String getAddress() {

+	    return port <= 0 ? host : host + ":" + port;

+	}

+

+	public String getPath() {

+		return path;

+	}

+	

+	public String getAbsolutePath() {

+        if (path != null && !path.startsWith("/")) {

+            return "/" + path;

+        }

+        return path;

+	}

+	

+	public URL setProtocol(String protocol) {

+	    return new URL(protocol, username, password, host, port, path, getParameters());

+	}

+

+    public URL setUsername(String username) {

+        return new URL(protocol, username, password, host, port, path, getParameters());

+    }

+

+    public URL setPassword(String password) {

+        return new URL(protocol, username, password, host, port, path, getParameters());

+    }

+    

+    public URL setAddress(String address) {

+        int i = address.lastIndexOf(':');

+        String host;

+        int port = this.port;

+        if (i >= 0) {

+            host = address.substring(0, i);

+            port = Integer.parseInt(address.substring(i + 1));

+        } else {

+            host = address;

+        }

+        return new URL(protocol, username, password, host, port, path, getParameters());

+    }

+

+    public URL setHost(String host) {

+        return new URL(protocol, username, password, host, port, path, getParameters());

+    }

+

+    public URL setPort(int port) {

+        return new URL(protocol, username, password, host, port, path, getParameters());

+    }

+

+    public URL setPath(String path) {

+        return new URL(protocol, username, password, host, port, path, getParameters());

+    }

+

+    public Map<String, String> getParameters() {

+        return parameters;

+    }

+    

+    public String getParameterAndDecoded(String key) {

+        return getParameterAndDecoded(key, null);

+    }

+    

+    public String getParameterAndDecoded(String key, String defaultValue) {

+        return decode(getParameter(key, defaultValue));

+    }

+

+    public String getParameter(String key) {

+        String value = parameters.get(key);

+        if (value == null || value.length() == 0) {

+            value = parameters.get(Constants.DEFAULT_KEY_PREFIX + key);

+        }

+        return value;

+    }

+

+    public String getParameter(String key, String defaultValue) {

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public String[] getParameter(String key, String[] defaultValue) {

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        return Constants.COMMA_SPLIT_PATTERN.split(value);

+    }

+    

+    private Map<String, Number> getNumbers() {

+        if (numbers == null) { // 允许并发重复创建

+            numbers = new ConcurrentHashMap<String, Number>();

+        }

+        return numbers;

+    }

+

+    public double getParameter(String key, double defaultValue) {

+        Number n = getNumbers().get(key);

+        if (n != null) {

+            return n.doubleValue();

+        }

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        double d = Double.parseDouble(value);

+        getNumbers().put(key, d);

+        return d;

+    }

+    

+    public float getParameter(String key, float defaultValue) {

+        Number n = getNumbers().get(key);

+        if (n != null) {

+            return n.floatValue();

+        }

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        float f = Float.parseFloat(value);

+        getNumbers().put(key, f);

+        return f;

+    }

+

+    public long getParameter(String key, long defaultValue) {

+        Number n = getNumbers().get(key);

+        if (n != null) {

+            return n.longValue();

+        }

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        long l = Long.parseLong(value);

+        getNumbers().put(key, l);

+        return l;

+    }

+

+    public int getParameter(String key, int defaultValue) {

+        Number n = getNumbers().get(key);

+        if (n != null) {

+            return n.intValue();

+        }

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        int i = Integer.parseInt(value);

+        getNumbers().put(key, i);

+        return i;

+    }

+

+    public short getParameter(String key, short defaultValue) {

+        Number n = getNumbers().get(key);

+        if (n != null) {

+            return n.shortValue();

+        }

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        short s = Short.parseShort(value);

+        getNumbers().put(key, s);

+        return s;

+    }

+

+    public byte getParameter(String key, byte defaultValue) {

+        Number n = getNumbers().get(key);

+        if (n != null) {

+            return n.byteValue();

+        }

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        byte b = Byte.parseByte(value);

+        getNumbers().put(key, b);

+        return b;

+    }

+

+    public float getPositiveParameter(String key, float defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        float value = getParameter(key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public double getPositiveParameter(String key, double defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        double value = getParameter(key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public long getPositiveParameter(String key, long defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        long value = getParameter(key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public int getPositiveParameter(String key, int defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        int value = getParameter(key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public short getPositiveParameter(String key, short defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        short value = getParameter(key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public byte getPositiveParameter(String key, byte defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        byte value = getParameter(key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public char getParameter(String key, char defaultValue) {

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        return value.charAt(0);

+    }

+

+    public boolean getParameter(String key, boolean defaultValue) {

+        String value = getParameter(key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        return Boolean.parseBoolean(value);

+    }

+

+    public boolean hasParameter(String key) {

+        String value = getParameter(key);

+        return value != null && value.length() > 0;

+    }

+

+    public String getMethodParameterAndDecoded(String method, String key) {

+        return URL.decode(getMethodParameter(method, key));

+    }

+

+    public String getMethodParameterAndDecoded(String method, String key, String defaultValue) {

+        return URL.decode(getMethodParameter(method, key, defaultValue));

+    }

+

+    public String getMethodParameter(String method, String key) {

+        String value = parameters.get(method + "." + key);

+        if (value == null || value.length() == 0) {

+            return getParameter(key);

+        }

+        return value;

+    }

+

+    public String getMethodParameter(String method, String key, String defaultValue) {

+        String value = getMethodParameter(method, key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public double getMethodParameter(String method, String key, double defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = getNumbers().get(methodKey);

+        if (n != null) {

+            return n.intValue();

+        }

+        String value = getMethodParameter(method, key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        double d = Double.parseDouble(value);

+        getNumbers().put(methodKey, d);

+        return d;

+    }

+

+    public float getMethodParameter(String method, String key, float defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = getNumbers().get(methodKey);

+        if (n != null) {

+            return n.intValue();

+        }

+        String value = getMethodParameter(method, key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        float f = Float.parseFloat(value);

+        getNumbers().put(methodKey, f);

+        return f;

+    }

+

+    public long getMethodParameter(String method, String key, long defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = getNumbers().get(methodKey);

+        if (n != null) {

+            return n.intValue();

+        }

+        String value = getMethodParameter(method, key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        long l = Long.parseLong(value);

+        getNumbers().put(methodKey, l);

+        return l;

+    }

+

+    public int getMethodParameter(String method, String key, int defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = getNumbers().get(methodKey);

+        if (n != null) {

+            return n.intValue();

+        }

+        String value = getMethodParameter(method, key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        int i = Integer.parseInt(value);

+        getNumbers().put(methodKey, i);

+        return i;

+    }

+

+    public short getMethodParameter(String method, String key, short defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = getNumbers().get(methodKey);

+        if (n != null) {

+            return n.shortValue();

+        }

+        String value = getMethodParameter(method, key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        short s = Short.parseShort(value);

+        getNumbers().put(methodKey, s);

+        return s;

+    }

+

+    public byte getMethodParameter(String method, String key, byte defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = getNumbers().get(methodKey);

+        if (n != null) {

+            return n.byteValue();

+        }

+        String value = getMethodParameter(method, key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        byte b = Byte.parseByte(value);

+        getNumbers().put(methodKey, b);

+        return b;

+    }

+

+    public double getMethodPositiveParameter(String method, String key, double defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        double value = getMethodParameter(method, key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public float getMethodPositiveParameter(String method, String key, float defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        float value = getMethodParameter(method, key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public long getMethodPositiveParameter(String method, String key, long defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        long value = getMethodParameter(method, key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+    

+    public int getMethodPositiveParameter(String method, String key, int defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        int value = getMethodParameter(method, key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public short getMethodPositiveParameter(String method, String key, short defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        short value = getMethodParameter(method, key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public byte getMethodPositiveParameter(String method, String key, byte defaultValue) {

+        if (defaultValue <= 0) {

+            throw new IllegalArgumentException("defaultValue <= 0");

+        }

+        byte value = getMethodParameter(method, key, defaultValue);

+        if (value <= 0) {

+            return defaultValue;

+        }

+        return value;

+    }

+

+    public char getMethodParameter(String method, String key, char defaultValue) {

+        String value = getMethodParameter(method, key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        return value.charAt(0);

+    }

+

+    public boolean getMethodParameter(String method, String key, boolean defaultValue) {

+        String value = getMethodParameter(method, key);

+        if (value == null || value.length() == 0) {

+            return defaultValue;

+        }

+        return Boolean.parseBoolean(value);

+    }

+

+    public boolean hasMethodParameter(String method, String key) {

+        if (method == null) {

+            String suffix = "." + key;

+            for (String fullKey : parameters.keySet()) {

+                if (fullKey.endsWith(suffix)) {

+                    return true;

+                }

+            }

+            return false;

+        }

+        if (key == null) {

+            String prefix = method + ".";

+            for (String fullKey : parameters.keySet()) {

+                if (fullKey.startsWith(prefix)) {

+                    return true;

+                }

+            }

+            return false;

+        }

+        String value = getMethodParameter(method, key);

+        return value != null && value.length() > 0;

+    }

+    

+    public boolean isAnyHost(){

+        if (Constants.ANYHOST.equals(host) || getParameter(Constants.ANYHOST_KEY, false)){

+            return true;

+        } else {

+            return false;

+        }

+    }

+    

+    public URL addParameterAndEncoded(String key, String value) {

+        if(value == null || value.length() == 0) {

+            return this;

+        }

+        return addParameter(key, encode(value));

+    }

+    

+    public URL addParameter(String key, boolean value) {

+        return addParameter(key, String.valueOf(value));

+    }

+

+    public URL addParameter(String key, char value) {

+        return addParameter(key, String.valueOf(value));

+    }

+

+    public URL addParameter(String key, byte value) {

+        return addParameter(key, String.valueOf(value));

+    }

+    

+    public URL addParameter(String key, short value) {

+        return addParameter(key, String.valueOf(value));

+    }

+    

+    public URL addParameter(String key, int value) {

+        return addParameter(key, String.valueOf(value));

+    }

+    

+    public URL addParameter(String key, long value) {

+        return addParameter(key, String.valueOf(value));

+    }

+

+    public URL addParameter(String key, float value) {

+        return addParameter(key, String.valueOf(value));

+    }

+    

+    public URL addParameter(String key, double value) {

+        return addParameter(key, String.valueOf(value));

+    }

+    

+    public URL addParameter(String key, Enum<?> value) {

+        if(value == null) return this;

+        return addParameter(key, String.valueOf(value));

+    }

+    

+    public URL addParameter(String key, Number value) {

+        if(value == null) return this;

+        return addParameter(key, String.valueOf(value));

+    }

+

+    public URL addParameter(String key, CharSequence value) {

+        if(value == null || value.length() == 0) return this;

+        return addParameter(key, String.valueOf(value));

+    }

+    

+    public URL addParameter(String key, String value) {

+        if (key == null || key.length() == 0

+                || value == null || value.length() == 0) {

+            return this;

+        }

+        Map<String, String> map = new HashMap<String, String>(getParameters());

+        map.put(key, value);

+        return new URL(protocol, username, password, host, port, path, map);

+    }

+    

+    public URL addParameterIfAbsent(String key, String value) {

+        if (key == null || key.length() == 0

+                || value == null || value.length() == 0) {

+            return this;

+        }

+        if (hasParameter(key)) {

+            return this;

+        }

+        Map<String, String> map = new HashMap<String, String>(getParameters());

+        map.put(key, value);

+        return new URL(protocol, username, password, host, port, path, map);

+    }

+    

+	/**

+	 * Add parameters to a new url.

+	 * 

+	 * @param parameters

+	 * @return A new URL 

+	 */

+    public URL addParameters(Map<String, String> parameters) {

+        if (parameters == null || parameters.size() == 0) {

+            return this;

+        }

+        Map<String, String> map = new HashMap<String, String>(getParameters());

+        map.putAll(parameters);

+        return new URL(protocol, username, password, host, port, path, map);

+    }

+    

+	public URL addParametersIfAbsent(Map<String, String> parameters) {

+		if (parameters == null || parameters.size() == 0) {

+			return this;

+		}

+		Map<String, String> map = new HashMap<String, String>(parameters);

+		map.putAll(getParameters());

+		return new URL(protocol, username, password, host, port, path, map);

+	}

+

+    public URL addParameters(String... pairs) {

+        if (pairs == null || pairs.length == 0) {

+            return this;

+        }

+        if (pairs.length % 2 != 0) {

+            throw new IllegalArgumentException("Map pairs can not be odd number.");

+        }

+        Map<String, String> map = new HashMap<String, String>();

+        int len = pairs.length / 2;

+        for (int i = 0; i < len; i ++) {

+            map.put(pairs[2 * i], pairs[2 * i + 1]);

+        }

+        return addParameters(map);

+    }

+    

+    public URL addParameterString(String query) {

+        if (query == null || query.length() == 0) {

+            return this;

+        }

+        return addParameters(StringUtils.parseQueryString(query));

+    }

+    

+    public URL removeParameter(String key) {

+        if (key == null || key.length() == 0) {

+            return this;

+        }

+        return removeParameters(key);

+    }

+    

+    public URL removeParameters(Collection<String> keys) {

+        if (keys == null || keys.size() == 0) {

+            return this;

+        }

+        return removeParameters(keys.toArray(new String[0]));

+    }

+

+	public URL removeParameters(String... keys) {

+	    if (keys == null || keys.length == 0) {

+            return this;

+        }

+        Map<String, String> map = new HashMap<String, String>(getParameters());

+        for (String key : keys) {

+            map.remove(key);

+        }

+        if (map.size() == getParameters().size()) {

+            return this;

+        }

+        return new URL(protocol, username, password, host, port, path, map);

+	}

+	

+	public URL cleatParameters() {

+        return new URL(protocol, username, password, host, port, path, new HashMap<String, String>());

+    }

+    

+	public String toString() {

+	    if (string != null) {

+            return string;

+        }

+        return string = buildString(false, true); // no show username and password

+    }

+

+    public String toString(String... parameters) {

+        return buildString(false, true, parameters); // no show username and password

+    }

+    

+    public String toIdentityString() {

+        if (identityString != null) {

+            return identityString;

+        }

+        return identityString = buildString(false, false); // only return identity message, see the method "equals" and "hashCode"

+	}

+

+    public String toIdentityString(String... parameters) {

+        return buildString(false, false, parameters); // only return identity message, see the method "equals" and "hashCode"

+    }

+    

+	public String toFullString() {

+	    if (fullString != null) {

+	        return fullString;

+	    }

+		return fullString = buildString(true, true);

+	}

+

+    public String toFullString(String... parameters) {

+        return buildString(true, true, parameters);

+    }

+    

+    public String toParameterString() {

+        if (parameterString != null) {

+            return parameterString;

+        }

+        return parameterString = toParameterString(new String[0]);

+    }

+    

+	public String toParameterString(String... parameters) {

+		StringBuilder buf = new StringBuilder();

+		buildParameters(buf, false, parameters);

+		return buf.toString();

+	}

+	

+	private void buildParameters(StringBuilder buf, boolean concat, String[] parameters) {

+	    if (getParameters() !=null && getParameters().size() > 0) {

+            List<String> includes = (parameters == null || parameters.length == 0 ? null : Arrays.asList(parameters));

+            boolean first = true;

+            for (Map.Entry<String, String> entry : new TreeMap<String, String>(getParameters()).entrySet()) {

+                if (entry.getKey() != null && entry.getKey().length() > 0

+                        && (includes == null || includes.contains(entry.getKey()))) {

+                    if (first) {

+                        if (concat) {

+                            buf.append("?");

+                        }

+                        first = false;

+                    } else {

+                        buf.append("&");

+                    }

+                    buf.append(entry.getKey());

+                    buf.append("=");

+                    buf.append(entry.getValue() == null ? "" : entry.getValue().trim());

+                }

+            }

+        }

+	}

+	

+	private String buildString(boolean u, boolean p, String... parameters) {

+		StringBuilder buf = new StringBuilder();

+		if (protocol != null && protocol.length() > 0) {

+			buf.append(protocol);

+			buf.append("://");

+		}

+		if (u && username != null && username.length() > 0) {

+			buf.append(username);

+			if (password != null && password.length() > 0) {

+				buf.append(":");

+				buf.append(password);

+			}

+			buf.append("@");

+		}

+		if(host != null && host.length() > 0) {

+    		buf.append(host);

+    		if (port > 0) {

+    			buf.append(":");

+    			buf.append(port);

+    		}

+		}

+		if (path != null && path.length() > 0) {

+			buf.append("/");

+			buf.append(path);

+		}

+		if (p) {

+		    buildParameters(buf, true, parameters);

+		}

+		return buf.toString();

+	}

+

+    public java.net.URL toJavaURL() {

+        try {

+            return new java.net.URL(toString());

+        } catch (MalformedURLException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+

+    public InetSocketAddress toInetSocketAddress() {

+        return new InetSocketAddress(host, port);

+    }

+

+    public String getServiceKey() {

+        String inf = getServiceInterface();

+        if (inf == null) return null;

+        StringBuilder buf = new StringBuilder();

+        String group = getParameter(Constants.GROUP_KEY);

+        if (group != null && group.length() > 0) {

+            buf.append(group).append("/");

+        }

+        buf.append(inf);

+        String version = getParameter(Constants.VERSION_KEY);

+        if (version != null && version.length() > 0) {

+            buf.append(":").append(version);

+        }

+        return buf.toString();

+    }

+

+    @Deprecated

+    public String getServiceName() {

+        return getServiceInterface();

+    }

+

+    public String getServiceInterface() {

+        return getParameter(Constants.INTERFACE_KEY, path);

+    }

+

+    public URL setServiceInterface(String service) {

+        return addParameter(Constants.INTERFACE_KEY, service);

+    }

+

+    /**

+     * @deprecated Replace to <code>getParameter(String, int)</code>

+     * @see #getParameter(String, int)

+     */

+    @Deprecated

+    public int getIntParameter(String key) {

+        return getParameter(key, 0);

+    }

+

+    /**

+     * @deprecated Replace to <code>getParameter(String, int)</code>

+     * @see #getParameter(String, int)

+     */

+    @Deprecated

+    public int getIntParameter(String key, int defaultValue) {

+        return getParameter(key, defaultValue);

+    }

+

+    /**

+     * @deprecated Replace to <code>getPositiveParameter(String, int)</code>

+     * @see #getPositiveParameter(String, int)

+     */

+    @Deprecated

+    public int getPositiveIntParameter(String key, int defaultValue) {

+        return getPositiveParameter(key, defaultValue);

+    }

+

+    /**

+     * @deprecated Replace to <code>getParameter(String, boolean)</code>

+     * @see #getParameter(String, boolean)

+     */

+    @Deprecated

+    public boolean getBooleanParameter(String key) {

+        return getParameter(key, false);

+    }

+

+    /**

+     * @deprecated Replace to <code>getParameter(String, boolean)</code>

+     * @see #getParameter(String, boolean)

+     */

+	@Deprecated

+	public boolean getBooleanParameter(String key, boolean defaultValue) {

+	    return getParameter(key, defaultValue);

+	}

+

+	/**

+     * @deprecated Replace to <code>getMethodParameter(String, String, int)</code>

+     * @see #getMethodParameter(String, String, int)

+     */

+    @Deprecated

+    public int getMethodIntParameter(String method, String key) {

+        return getMethodParameter(method, key, 0);

+    }

+

+    /**

+     * @deprecated Replace to <code>getMethodParameter(String, String, int)</code>

+     * @see #getMethodParameter(String, String, int)

+     */

+    @Deprecated

+    public int getMethodIntParameter(String method, String key, int defaultValue) {

+        return getMethodParameter(method, key, defaultValue);

+    }

+

+    /**

+     * @deprecated Replace to <code>getMethodPositiveParameter(String, String, int)</code>

+     * @see #getMethodPositiveParameter(String, String, int)

+     */

+    @Deprecated

+    public int getMethodPositiveIntParameter(String method, String key, int defaultValue) {

+        return getMethodPositiveParameter(method, key, defaultValue);

+    }

+

+    /**

+     * @deprecated Replace to <code>getMethodParameter(String, String, boolean)</code>

+     * @see #getMethodParameter(String, String, boolean)

+     */

+    @Deprecated

+    public boolean getMethodBooleanParameter(String method, String key) {

+        return getMethodParameter(method, key, false);

+    }

+

+    /**

+     * @deprecated Replace to <code>getMethodParameter(String, String, boolean)</code>

+     * @see #getMethodParameter(String, String, boolean)

+     */

+    @Deprecated

+    public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) {

+        return getMethodParameter(method, key, defaultValue);

+    }

+

+    public static String encode(String value) {

+        if (value == null || value.length() == 0) { 

+            return "";

+        }

+        try {

+            return URLEncoder.encode(value, "UTF-8");

+        } catch (UnsupportedEncodingException e) {

+            throw new RuntimeException(e.getMessage(), e);

+        }

+    }

+    

+    public static String decode(String value) {

+        if (value == null || value.length() == 0) { 

+            return "";

+        }

+        try {

+            return URLDecoder.decode(value, "UTF-8");

+        } catch (UnsupportedEncodingException e) {

+            throw new RuntimeException(e.getMessage(), e);

+        }

+    }

+

+    public int hashCode() {

+        final int prime = 31;

+        int result = 1;

+        result = prime * result + ((host == null) ? 0 : host.hashCode());

+        result = prime * result + ((path == null) ? 0 : path.hashCode());

+        result = prime * result + port;

+        result = prime * result

+                + ((protocol == null) ? 0 : protocol.hashCode());

+        return result;

+    }

+

+    public boolean equals(Object obj) {

+        if (this == obj)

+            return true;

+        if (obj == null)

+            return false;

+        if (getClass() != obj.getClass())

+            return false;

+        URL other = (URL) obj;

+        if (host == null) {

+            if (other.host != null)

+                return false;

+        } else if (!host.equals(other.host))

+            return false;

+        if (path == null) {

+            if (other.path != null)

+                return false;

+        } else if (!path.equals(other.path))

+            return false;

+        if (port != other.port)

+            return false;

+        if (protocol == null) {

+            if (other.protocol != null)

+                return false;

+        } else if (!protocol.equals(other.protocol))

+            return false;

+        return true;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Version.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Version.java
new file mode 100644
index 0000000..9e8e5fb
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Version.java
@@ -0,0 +1,135 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common;
+
+import java.net.URL;

+import java.util.Enumeration;

+import java.util.HashSet;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+
+/**
+ * Version
+ * 
+ * @author william.liangf
+ */
+public final class Version {
+
+    private Version() {}
+
+    private static final Logger logger = LoggerFactory.getLogger(Version.class);
+
+    private static final String VERSION = getVersion(Version.class, "2.0.0");
+
+    private static final boolean INTERNAL = hasResource("com/alibaba/dubbo/registry/support/remote/RemoteRegistry.class");
+
+    private static final boolean COMPATIBLE = hasResource("com/alibaba/dubbo/rpc/dubbo/internal/DubboRequest.class");
+
+    static {
+        // 检查是否存在重复的jar包
+    	Version.checkDuplicate(Version.class);

+	}
+
+    public static String getVersion(){
+    	return VERSION;
+    }
+    
+    public static boolean isInternalVersion() {
+        return INTERNAL;
+    }
+
+    public static boolean isCompatibleVersion() {
+        return COMPATIBLE;
+    }
+    
+    private static boolean hasResource(String path) {
+        try {
+            return Version.class.getClassLoader().getResource(path) != null;
+        } catch (Throwable t) {
+            return false;
+        }
+    }
+    
+    public static String getVersion(Class<?> cls, String defaultVersion) {
+        try {
+            // 首先查找MANIFEST.MF规范中的版本号
+            String version = cls.getPackage().getImplementationVersion();
+            if (version == null || version.length() == 0) {
+                version = cls.getPackage().getSpecificationVersion();
+            }
+            if (version == null || version.length() == 0) {
+                // 如果规范中没有版本号,基于jar包名获取版本号
+                String file = cls.getProtectionDomain().getCodeSource().getLocation().getFile();
+                if (file != null && file.length() > 0 && file.endsWith(".jar")) {
+                    file = file.substring(0, file.length() - 4);
+                    int i = file.lastIndexOf('/');
+                    if (i >= 0) {
+                        file = file.substring(i + 1);
+                    }
+                    i = file.indexOf("-");
+                    if (i >= 0) {
+                        file = file.substring(i + 1);
+                    }
+                    while (file.length() > 0 && ! Character.isDigit(file.charAt(0))) {
+                        i = file.indexOf("-");
+                        if (i >= 0) {
+                            file = file.substring(i + 1);
+                        } else {
+                            break;
+                        }
+                    }
+                    version = file;
+                }
+            }
+            // 返回版本号,如果为空返回缺省版本号
+            return version == null || version.length() == 0 ? defaultVersion : version;
+        } catch (Throwable e) { // 防御性容错
+            // 忽略异常,返回缺省版本号
+            logger.error(e.getMessage(), e);
+            return defaultVersion;
+        }
+    }
+
+	public static void checkDuplicate(Class<?> cls) {
+		checkDuplicate(cls.getName().replace('.', '/') + ".class");
+	}
+
+	public static void checkDuplicate(String path) {
+		try {
+			// 在ClassPath搜文件
+			Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(path);
+			Set<String> files = new HashSet<String>();
+			while (urls.hasMoreElements()) {
+				URL url = urls.nextElement();
+				if (url != null) {
+					String file = url.getFile();
+					if (file != null && file.length() > 0) {
+						files.add(file);
+					}
+				}
+			}
+			// 如果有多个,就表示重复
+			if (files.size() > 1) {
+				logger.error("Duplicate class " + path + " in " + files.size() + " jar " + files);
+			}
+		} catch (Throwable e) { // 防御性容错
+			logger.error(e.getMessage(), e);
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.java
new file mode 100644
index 0000000..efe08a0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.java
@@ -0,0 +1,386 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;

+

+import java.lang.reflect.Constructor;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.ArrayList;

+import java.util.HashSet;

+import java.util.LinkedList;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.atomic.AtomicLong;

+

+import javassist.CannotCompileException;

+import javassist.ClassPool;

+import javassist.CtClass;

+import javassist.CtConstructor;

+import javassist.CtField;

+import javassist.CtMethod;

+import javassist.CtNewConstructor;

+import javassist.CtNewMethod;

+import javassist.LoaderClassPath;

+import javassist.NotFoundException;

+

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+

+/**

+ * ClassGenerator

+ * 

+ * @author qian.lei

+ */

+

+public final class ClassGenerator

+{

+	public static interface DC{} // dynamic class tag interface.

+

+	private static final AtomicLong CLASS_NAME_COUNTER = new AtomicLong(0);

+

+	private static final String SIMPLE_NAME_TAG = "<init>";

+

+	private static final Map<ClassLoader, ClassPool> POOL_MAP = new ConcurrentHashMap<ClassLoader, ClassPool>(); //ClassLoader - ClassPool

+

+	public static ClassGenerator newInstance()

+	{

+		return new ClassGenerator(getClassPool(Thread.currentThread().getContextClassLoader()));

+	}

+

+	public static ClassGenerator newInstance(ClassLoader loader)

+	{

+		return new ClassGenerator(getClassPool(loader));

+	}

+

+	public static boolean isDynamicClass(Class<?> cl)

+	{

+		return ClassGenerator.DC.class.isAssignableFrom(cl);

+	}

+

+	public static ClassPool getClassPool(ClassLoader loader)

+	{

+		if( loader == null )

+			return ClassPool.getDefault();

+

+		ClassPool pool = POOL_MAP.get(loader);

+		if( pool == null )

+		{

+			pool = new ClassPool(true);

+			pool.appendClassPath(new LoaderClassPath(loader));

+			POOL_MAP.put(loader, pool);

+		}

+		return pool;

+	}

+

+	private ClassPool mPool;

+

+	private CtClass mCtc;

+

+	private String mClassName, mSuperClass;

+

+	private Set<String> mInterfaces;

+

+	private List<String> mFields, mConstructors, mMethods;

+

+	private Map<String, Method> mCopyMethods; // <method desc,method instance>

+

+	private Map<String, Constructor<?>> mCopyConstructors; // <constructor desc,constructor instance>

+

+	private boolean mDefaultConstructor = false;

+

+	private ClassGenerator(){}

+

+	private ClassGenerator(ClassPool pool)

+	{

+		mPool = pool;

+	}

+

+	public String getClassName()

+	{

+		return mClassName;

+	}

+

+	public ClassGenerator setClassName(String name)

+	{

+		mClassName = name;

+		return this;

+	}

+

+	public ClassGenerator addInterface(String cn)

+	{

+		if( mInterfaces == null )

+			mInterfaces = new HashSet<String>();

+		mInterfaces.add(cn);

+		return this;

+	}

+

+	public ClassGenerator addInterface(Class<?> cl)

+	{

+		return addInterface(cl.getName());

+	}

+

+	public ClassGenerator setSuperClass(String cn)

+	{

+		mSuperClass = cn;

+		return this;

+	}

+

+	public ClassGenerator setSuperClass(Class<?> cl)

+	{

+		mSuperClass = cl.getName();

+		return this;

+	}

+

+	public ClassGenerator addField(String code)

+	{

+		if( mFields == null )

+			mFields = new ArrayList<String>();

+		mFields.add(code);

+		return this;

+	}

+

+	public ClassGenerator addField(String name, int mod, Class<?> type)

+	{

+		return addField(name, mod, type, null);

+	}

+

+	public ClassGenerator addField(String name, int mod, Class<?> type, String def)

+	{

+		StringBuilder sb = new StringBuilder();

+		sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(type)).append(' ');

+		sb.append(name);

+		if( def != null && def.length() > 0 )

+		{

+			sb.append('=');

+			sb.append(def);

+		}

+		sb.append(';');

+		return addField(sb.toString());

+	}

+

+	public ClassGenerator addMethod(String code)

+	{

+		if( mMethods == null )

+			mMethods = new ArrayList<String>();

+		mMethods.add(code);

+		return this;

+	}

+

+	public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, String body)

+	{

+		return addMethod(name, mod, rt, pts, null, body);

+	}

+

+	public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, Class<?>[] ets, String body)

+	{

+		StringBuilder sb = new StringBuilder();

+		sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(rt)).append(' ').append(name);

+		sb.append('(');

+		for(int i=0;i<pts.length;i++)

+		{

+			if( i > 0 )

+				sb.append(',');

+			sb.append(ReflectUtils.getName(pts[i]));

+			sb.append(" arg").append(i);

+		}

+		sb.append(')');

+		if( ets != null && ets.length > 0 )

+		{

+			sb.append(" throws ");

+			for(int i=0;i<ets.length;i++)

+			{

+				if( i > 0 )

+					sb.append(',');

+				sb.append(ReflectUtils.getName(ets[i]));

+			}

+		}

+		sb.append('{').append(body).append('}');

+		return addMethod(sb.toString());

+	}

+

+	public ClassGenerator addMethod(Method m)

+	{

+		addMethod(m.getName(), m);

+		return this;

+	}

+

+	public ClassGenerator addMethod(String name, Method m)

+	{

+		String desc = name + ReflectUtils.getDescWithoutMethodName(m);

+		addMethod(':' + desc);

+		if( mCopyMethods == null )

+			mCopyMethods = new ConcurrentHashMap<String, Method>(8);

+		mCopyMethods.put(desc, m);

+		return this;

+	}

+

+	public ClassGenerator addConstructor(String code)

+	{

+		if( mConstructors == null )

+			mConstructors = new LinkedList<String>();

+		mConstructors.add(code);

+		return this;

+	}

+

+	public ClassGenerator addConstructor(int mod, Class<?>[] pts, String body)

+	{

+		return addConstructor(mod, pts, null, body);

+	}

+

+	public ClassGenerator addConstructor(int mod, Class<?>[] pts, Class<?>[] ets, String body)

+	{

+		StringBuilder sb = new StringBuilder();

+		sb.append(modifier(mod)).append(' ').append(SIMPLE_NAME_TAG);

+		sb.append('(');

+		for(int i=0;i<pts.length;i++)

+		{

+			if( i > 0 )

+				sb.append(',');

+			sb.append(ReflectUtils.getName(pts[i]));

+			sb.append(" arg").append(i);

+		}

+		sb.append(')');

+		if( ets != null && ets.length > 0 )

+		{

+			sb.append(" throws ");

+			for(int i=0;i<ets.length;i++)

+			{

+				if( i > 0 )

+					sb.append(',');

+				sb.append(ReflectUtils.getName(ets[i]));

+			}

+		}

+		sb.append('{').append(body).append('}');

+		return addConstructor(sb.toString());

+	}

+

+	public ClassGenerator addConstructor(Constructor<?> c)

+	{

+		String desc = ReflectUtils.getDesc(c);

+		addConstructor(":"+desc);

+		if( mCopyConstructors == null )

+			mCopyConstructors = new ConcurrentHashMap<String, Constructor<?>>(4);

+		mCopyConstructors.put(desc, c);

+		return this;

+	}

+

+	public ClassGenerator addDefaultConstructor()

+	{

+		mDefaultConstructor = true;

+		return this;

+	}

+

+	public ClassPool getClassPool() {

+	    return mPool;

+	}

+

+	public Class<?> toClass()

+	{

+		if( mCtc != null )

+			mCtc.detach();

+		long id = CLASS_NAME_COUNTER.getAndIncrement();

+		try

+		{

+			CtClass ctcs = mSuperClass == null ? null : mPool.get(mSuperClass);

+			if( mClassName == null )

+				mClassName = ( mSuperClass == null || javassist.Modifier.isPublic(ctcs.getModifiers())

+						? ClassGenerator.class.getName() : mSuperClass + "$sc" ) + id;

+			mCtc = mPool.makeClass(mClassName);

+			if( mSuperClass != null )

+				mCtc.setSuperclass(ctcs);

+			mCtc.addInterface(mPool.get(DC.class.getName())); // add dynamic class tag.

+			if( mInterfaces != null )

+				for( String cl : mInterfaces ) mCtc.addInterface(mPool.get(cl));

+			if( mFields != null )

+				for( String code : mFields ) mCtc.addField(CtField.make(code, mCtc));

+			if( mMethods != null )

+			{

+				for( String code : mMethods )

+				{

+					if( code.charAt(0) == ':' )

+						mCtc.addMethod(CtNewMethod.copy(getCtMethod(mCopyMethods.get(code.substring(1))), code.substring(1, code.indexOf('(')), mCtc, null));

+					else

+						mCtc.addMethod(CtNewMethod.make(code, mCtc));

+				}

+			}

+			if( mDefaultConstructor )

+				mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));

+			if( mConstructors != null )

+			{

+				for( String code : mConstructors )

+				{

+					if( code.charAt(0) == ':' )

+					{

+						mCtc.addConstructor(CtNewConstructor.copy(getCtConstructor(mCopyConstructors.get(code.substring(1))), mCtc, null));

+					}

+					else

+					{

+						String[] sn = mCtc.getSimpleName().split("\\$+"); // inner class name include $.

+						mCtc.addConstructor(CtNewConstructor.make(code.replaceFirst(SIMPLE_NAME_TAG, sn[sn.length-1]), mCtc));

+					}

+				}

+			}

+			return mCtc.toClass();

+		}

+		catch(RuntimeException e)

+		{

+			throw e;

+		}

+		catch(NotFoundException e)

+		{

+			throw new RuntimeException(e.getMessage(), e);

+		}

+		catch(CannotCompileException e)

+		{

+			throw new RuntimeException(e.getMessage(), e);

+		}

+	}

+

+	public void release()

+	{

+		if( mCtc != null ) mCtc.detach();

+		if( mInterfaces != null ) mInterfaces.clear();

+		if( mFields != null ) mFields.clear();

+		if( mMethods != null ) mMethods.clear();

+		if( mConstructors != null ) mConstructors.clear();

+		if( mCopyMethods != null ) mCopyMethods.clear();

+		if( mCopyConstructors != null ) mCopyConstructors.clear();

+	}

+

+	private CtClass getCtClass(Class<?> c) throws NotFoundException

+	{

+		return mPool.get(c.getName());

+	}

+

+	private CtMethod getCtMethod(Method m) throws NotFoundException

+	{

+		return getCtClass(m.getDeclaringClass()).getMethod(m.getName(),ReflectUtils.getDescWithoutMethodName(m));

+	}

+

+	private CtConstructor getCtConstructor(Constructor<?> c) throws NotFoundException

+	{

+		return getCtClass(c.getDeclaringClass()).getConstructor(ReflectUtils.getDesc(c));

+	}

+

+	private static String modifier(int mod)

+	{

+		if( Modifier.isPublic(mod) ) return "public";

+		if( Modifier.isProtected(mod) ) return "protected";

+		if( Modifier.isPrivate(mod) ) return "private";

+		return "";

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Mixin.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Mixin.java
new file mode 100644
index 0000000..751a662
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Mixin.java
@@ -0,0 +1,241 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;

+

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.HashSet;

+import java.util.Set;

+import java.util.concurrent.atomic.AtomicLong;

+

+import com.alibaba.dubbo.common.utils.ClassHelper;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+

+/**

+ * Mixin

+ * 

+ * @author qian.lei

+ */

+

+public abstract class Mixin

+{

+	private static AtomicLong MIXIN_CLASS_COUNTER = new AtomicLong(0);

+

+	private static final String PACKAGE_NAME = Mixin.class.getPackage().getName();

+

+	public static interface MixinAware{ void setMixinInstance(Object instance); }

+

+	/**

+	 * mixin interface and delegates.

+	 * all class must be public.

+	 * 

+	 * @param ics interface class array.

+	 * @param dc delegate class.

+	 * @return Mixin instance.

+	 */

+	public static Mixin mixin(Class<?>[] ics, Class<?> dc)

+	{

+		return mixin(ics, new Class[]{dc});

+	}

+

+	/**

+	 * mixin interface and delegates.

+	 * all class must be public.

+	 * 

+	 * @param ics interface class array.

+	 * @param dc delegate class.

+	 * @param cl class loader.

+	 * @return Mixin instance.

+	 */

+	public static Mixin mixin(Class<?>[] ics, Class<?> dc, ClassLoader cl)

+	{

+		return mixin(ics, new Class[]{dc}, cl);

+	}

+

+	/**

+	 * mixin interface and delegates.

+	 * all class must be public.

+	 * 

+	 * @param ics interface class array.

+	 * @param dcs delegate class array.

+	 * @return Mixin instance.

+	 */

+	public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs)

+	{

+		return mixin(ics, dcs, ClassHelper.getClassLoader(ics[0]));

+	}

+

+	/**

+	 * mixin interface and delegates.

+	 * all class must be public.

+	 * 

+	 * @param ics interface class array.

+	 * @param dcs delegate class array.

+	 * @param cl class loader.

+	 * @return Mixin instance.

+	 */

+	public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs, ClassLoader cl)

+	{

+		assertInterfaceArray(ics);

+

+		long id = MIXIN_CLASS_COUNTER.getAndIncrement();

+		String pkg = null;

+		ClassGenerator ccp = null, ccm = null;

+		try

+		{

+			ccp = ClassGenerator.newInstance(cl);

+

+			// impl constructor

+			StringBuilder code = new StringBuilder();

+			for(int i=0;i<dcs.length;i++)

+			{

+				if( !Modifier.isPublic(dcs[i].getModifiers()) )

+				{

+					String npkg = dcs[i].getPackage().getName();

+					if( pkg == null )

+					{

+						pkg = npkg;

+					}

+					else

+					{

+						if( !pkg.equals(npkg)  )

+							throw new IllegalArgumentException("non-public interfaces class from different packages");

+					}

+				}

+

+				ccp.addField("private " + dcs[i].getName() + " d" + i + ";");

+

+				code.append("d").append(i).append(" = (").append(dcs[i].getName()).append(")$1[").append(i).append("];\n");

+				if( MixinAware.class.isAssignableFrom(dcs[i]) )

+					code.append("d").append(i).append(".setMixinInstance(this);\n");

+			}

+			ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{ Object[].class }, code.toString());

+

+			// impl methods.

+			Set<String> worked = new HashSet<String>();

+			for(int i=0;i<ics.length;i++)

+			{

+				if( !Modifier.isPublic(ics[i].getModifiers()) )

+				{

+					String npkg = ics[i].getPackage().getName();

+					if( pkg == null )

+					{

+						pkg = npkg;

+					}

+					else

+					{

+						if( !pkg.equals(npkg)  )

+							throw new IllegalArgumentException("non-public delegate class from different packages");

+					}

+				}

+

+				ccp.addInterface(ics[i]);

+

+				for( Method method : ics[i].getMethods() )

+				{

+					if( "java.lang.Object".equals(method.getDeclaringClass().getName()) )

+						continue;

+

+					String desc = ReflectUtils.getDesc(method);

+					if( worked.contains(desc) )

+						continue;

+					worked.add(desc);

+

+					int ix = findMethod(dcs, desc);

+					if( ix < 0 )

+						throw new RuntimeException("Missing method [" + desc + "] implement.");

+

+					Class<?> rt = method.getReturnType();

+					String mn = method.getName();

+					if( Void.TYPE.equals(rt) )

+						ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),

+								"d" + ix + "." + mn + "($$);");

+					else

+						ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),

+								"return ($r)d" + ix + "." + mn + "($$);");

+				}

+			}

+

+			if( pkg == null )

+				pkg = PACKAGE_NAME;

+

+			// create MixinInstance class.

+			String micn = pkg + ".mixin" + id;

+			ccp.setClassName(micn);

+			ccp.toClass();

+

+			// create Mixin class.

+			String fcn = Mixin.class.getName() + id;

+			ccm = ClassGenerator.newInstance(cl);

+			ccm.setClassName(fcn);

+			ccm.addDefaultConstructor();

+			ccm.setSuperClass(Mixin.class.getName());

+			ccm.addMethod("public Object newInstance(Object[] delegates){ return new " + micn + "($1); }");

+			Class<?> mixin = ccm.toClass();

+			return (Mixin)mixin.newInstance();

+		}

+		catch(RuntimeException e)

+		{

+			throw e;

+		}

+		catch(Exception e)

+		{

+			throw new RuntimeException(e.getMessage(), e);

+		}

+		finally

+		{

+			// release ClassGenerator

+			if( ccp != null )

+				ccp.release();

+			if( ccm != null )

+				ccm.release();

+		}

+	}

+

+	/**

+	 * new Mixin instance.

+	 * 

+	 * @param ds delegates instance.

+	 * @return instance.

+	 */

+	abstract public Object newInstance(Object[] ds);

+

+	protected Mixin(){}

+

+	private static int findMethod(Class<?>[] dcs, String desc)

+	{

+		Class<?> cl;

+		Method[] methods;

+		for(int i=0;i<dcs.length;i++)

+		{

+			cl = dcs[i];

+			methods = cl.getMethods();

+			for( Method method : methods )

+			{

+				if( desc.equals(ReflectUtils.getDesc(method)) )

+					return i;

+			}

+		}

+		return -1;

+	}

+

+	private static void assertInterfaceArray(Class<?>[] ics)

+	{

+		for(int i=0;i<ics.length;i++)

+			if( !ics[i].isInterface() )

+				throw new RuntimeException("Class " + ics[i].getName() + " is not a interface.");

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchMethodException.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchMethodException.java
new file mode 100644
index 0000000..a8083e8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchMethodException.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;

+

+/**

+ * NoSuchMethodException.

+ * 

+ * @author qian.lei

+ */

+

+public class NoSuchMethodException extends RuntimeException

+{

+	private static final long serialVersionUID = -2725364246023268766L;

+

+	public NoSuchMethodException()

+	{

+		super();

+	}

+

+	public NoSuchMethodException(String msg)

+	{

+		super(msg);

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchPropertyException.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchPropertyException.java
new file mode 100644
index 0000000..1384fdd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchPropertyException.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;

+

+/**

+ * NoSuchPropertyException.

+ * 

+ * @author qian.lei

+ */

+

+public class NoSuchPropertyException extends RuntimeException

+{

+	private static final long serialVersionUID = -2725364246023268766L;

+

+	public NoSuchPropertyException()

+	{

+		super();

+	}

+

+	public NoSuchPropertyException(String msg)

+	{

+		super(msg);

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Proxy.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Proxy.java
new file mode 100644
index 0000000..03b27f9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Proxy.java
@@ -0,0 +1,286 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;

+

+import java.lang.ref.Reference;

+import java.lang.ref.WeakReference;

+import java.lang.reflect.InvocationHandler;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.WeakHashMap;

+import java.util.concurrent.atomic.AtomicLong;

+

+import com.alibaba.dubbo.common.utils.ClassHelper;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+

+/**

+ * Proxy.

+ * 

+ * @author qian.lei

+ */

+

+public abstract class Proxy

+{

+	private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0);

+

+	private static final String PACKAGE_NAME = Proxy.class.getPackage().getName();

+

+	public static final InvocationHandler RETURN_NULL_INVOKER = new InvocationHandler(){

+		public Object invoke(Object proxy, Method method, Object[] args){ return null; }

+	};

+

+	public static final InvocationHandler THROW_UNSUPPORTED_INVOKER = new InvocationHandler(){

+		public Object invoke(Object proxy, Method method, Object[] args){ throw new UnsupportedOperationException("Method [" + ReflectUtils.getName(method) + "] unimplemented."); }

+	};

+

+	private static final Map<ClassLoader, Map<String, Object>> ProxyCacheMap = new WeakHashMap<ClassLoader, Map<String, Object>>();

+

+	private static final Object PendingGenerationMarker = new Object();

+

+	/**

+	 * Get proxy.

+	 * 

+	 * @param ics interface class array.

+	 * @return Proxy instance.

+	 */

+	public static Proxy getProxy(Class<?>... ics)

+	{

+		return getProxy(ClassHelper.getClassLoader(ics[0]), ics);

+	}

+

+	/**

+	 * Get proxy.

+	 * @param cl class loader.

+	 * @param ics interface class array.

+	 * 

+	 * @return Proxy instance.

+	 */

+	public static Proxy getProxy(ClassLoader cl, Class<?>... ics)

+	{

+		if( ics.length > 65535 )

+			throw new IllegalArgumentException("interface limit exceeded");

+		

+		StringBuilder sb = new StringBuilder();

+		for(int i=0;i<ics.length;i++)

+		{

+			String itf = ics[i].getName();

+			if( !ics[i].isInterface() )

+				throw new RuntimeException(itf + " is not a interface.");

+

+			Class<?> tmp = null;

+			try

+			{

+				tmp = Class.forName(itf, false, cl);

+			}

+			catch(ClassNotFoundException e)

+			{}

+

+			if( tmp != ics[i] )

+				throw new IllegalArgumentException(ics[i] + " is not visible from class loader");

+

+		    sb.append(itf).append(';');

+		}

+

+		// use interface class name list as key.

+		String key = sb.toString();

+

+		// get cache by class loader.

+		Map<String, Object> cache;

+		synchronized( ProxyCacheMap )

+		{

+			cache = ProxyCacheMap.get(cl);

+			if( cache == null )

+		    {

+				cache = new HashMap<String, Object>();

+				ProxyCacheMap.put(cl, cache);

+		    }

+		}

+

+		Proxy proxy = null;

+		synchronized( cache )

+		{

+			do

+			{

+				Object value = cache.get(key);

+				if( value instanceof Reference<?> )

+				{

+					proxy = (Proxy)((Reference<?>)value).get();

+					if( proxy != null )

+						return proxy;

+				}

+

+				if( value == PendingGenerationMarker )

+				{

+					try{ cache.wait(); }catch(InterruptedException e){}

+				}

+				else

+				{

+					cache.put(key, PendingGenerationMarker);

+					break;

+				}

+			}

+			while( true );

+		}

+

+		long id = PROXY_CLASS_COUNTER.getAndIncrement();

+		String pkg = null;

+		ClassGenerator ccp = null, ccm = null;

+		try

+		{

+			ccp = ClassGenerator.newInstance(cl);

+

+			Set<String> worked = new HashSet<String>();

+			List<Method> methods = new ArrayList<Method>();

+

+			for(int i=0;i<ics.length;i++)

+			{

+				if( !Modifier.isPublic(ics[i].getModifiers()) )

+				{

+					String npkg = ics[i].getPackage().getName();

+					if( pkg == null )

+					{

+						pkg = npkg;

+					}

+					else

+					{

+						if( !pkg.equals(npkg)  )

+							throw new IllegalArgumentException("non-public interfaces from different packages");

+					}

+				}

+				ccp.addInterface(ics[i]);

+

+				for( Method method : ics[i].getMethods() )

+				{

+					String desc = ReflectUtils.getDesc(method);

+					if( worked.contains(desc) )

+						continue;

+					worked.add(desc);

+

+					int ix = methods.size();

+					Class<?> rt = method.getReturnType();

+					Class<?>[] pts = method.getParameterTypes();

+

+					StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");

+					for(int j=0;j<pts.length;j++)

+						code.append(" args[").append(j).append("] = ($w)$").append(j+1).append(";");

+					code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");

+					if( !Void.TYPE.equals(rt) )

+						code.append(" return ").append(asArgument(rt, "ret")).append(";");

+

+					methods.add(method);

+					ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());

+				}

+			}

+

+			if( pkg == null )

+				pkg = PACKAGE_NAME;

+

+			// create ProxyInstance class.

+			String pcn = pkg + ".proxy" + id;

+			ccp.setClassName(pcn);

+			ccp.addField("public static java.lang.reflect.Method[] methods;");

+			ccp.addField("private " + InvocationHandler.class.getName() + " handler;");

+			ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{ InvocationHandler.class }, new Class<?>[0], "handler=$1;");

+			Class<?> clazz = ccp.toClass();

+			clazz.getField("methods").set(null, methods.toArray(new Method[0]));

+

+			// create Proxy class.

+			String fcn = Proxy.class.getName() + id;

+			ccm = ClassGenerator.newInstance(cl);

+			ccm.setClassName(fcn);

+			ccm.addDefaultConstructor();

+			ccm.setSuperClass(Proxy.class);

+			ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");

+			Class<?> pc = ccm.toClass();

+			proxy = (Proxy)pc.newInstance();

+		}

+		catch(RuntimeException e)

+		{

+			throw e;

+		}

+		catch(Exception e)

+		{

+			throw new RuntimeException(e.getMessage(), e);

+		}

+		finally

+		{

+			// release ClassGenerator

+			if( ccp != null )

+				ccp.release();

+			if( ccm != null )

+				ccm.release();

+			synchronized( cache )

+			{

+				if( proxy == null )

+					cache.remove(key);

+				else

+					cache.put(key, new WeakReference<Proxy>(proxy));

+				cache.notifyAll();

+			}

+		}

+		return proxy;

+	}

+

+	/**

+	 * get instance with default handler.

+	 * 

+	 * @return instance.

+	 */

+	public Object newInstance()

+	{

+		return newInstance(THROW_UNSUPPORTED_INVOKER);

+	}

+

+	/**

+	 * get instance with special handler.

+	 * 

+	 * @return instance.

+	 */

+	abstract public Object newInstance(InvocationHandler handler);

+

+	protected Proxy(){}

+

+	private static String asArgument(Class<?> cl, String name)

+	{

+		if( cl.isPrimitive() )

+		{

+			if( Boolean.TYPE == cl )

+				return name + "==null?false:((Boolean)" + name + ").booleanValue()";

+			if( Byte.TYPE == cl )

+				return name + "==null?(byte)0:((Byte)" + name + ").byteValue()";

+			if( Character.TYPE == cl )

+				return name + "==null?(char)0:((Character)" + name + ").charValue()";

+			if( Double.TYPE == cl )

+				return name + "==null?(double)0:((Double)" + name + ").doubleValue()";

+			if( Float.TYPE == cl )

+				return name + "==null?(float)0:((Float)" + name + ").floatValue()";

+			if( Integer.TYPE == cl )

+				return name + "==null?(int)0:((Integer)" + name + ").intValue()";

+			if( Long.TYPE == cl )

+				return name + "==null?(long)0:((Long)" + name + ").longValue()";

+			if( Short.TYPE == cl )

+				return name + "==null?(short)0:((Short)" + name + ").shortValue()";

+			throw new RuntimeException(name+" is unknown primitive type."); 

+		}

+		return "(" + ReflectUtils.getName(cl) + ")"+name;

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Wrapper.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Wrapper.java
new file mode 100644
index 0000000..3a7b488
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Wrapper.java
@@ -0,0 +1,410 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;

+

+import java.lang.reflect.Field;

+import java.lang.reflect.InvocationTargetException;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.LinkedHashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.atomic.AtomicLong;

+import java.util.regex.Matcher;

+

+import com.alibaba.dubbo.common.utils.ClassHelper;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+

+/**

+ * Wrapper.

+ * 

+ * @author qian.lei

+ */

+

+public abstract class Wrapper

+{

+	private static AtomicLong WRAPPER_CLASS_COUNTER = new AtomicLong(0);

+

+	private static final Map<Class<?>, Wrapper> WRAPPER_MAP = new ConcurrentHashMap<Class<?>, Wrapper>(); //class wrapper map

+

+	private static final String[] EMPTY_STRING_ARRAY = new String[0];

+

+	private static final String[] OBJECT_METHODS = new String[]{"getClass", "hashCode", "toString", "equals"};

+

+	private static final Wrapper OBJECT_WRAPPER = new Wrapper(){

+		public String[] getMethodNames(){ return OBJECT_METHODS; }

+		public String[] getDeclaredMethodNames(){ return OBJECT_METHODS; }

+		public String[] getPropertyNames(){ return EMPTY_STRING_ARRAY; }

+		public Class<?> getPropertyType(String pn){ return null; }

+		public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException{ throw new NoSuchPropertyException("Property [" + pn + "] not found."); }

+		public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException{ throw new NoSuchPropertyException("Property [" + pn + "] not found."); }

+		public boolean hasProperty(String name){ return false; }

+		public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException

+		{

+			if( "getClass".equals(mn) ) return instance.getClass();

+			if( "hashCode".equals(mn) ) return instance.hashCode();

+			if( "toString".equals(mn) ) return instance.toString();

+			if( "equals".equals(mn) )

+			{

+				if( args.length == 1 ) return instance.equals(args[0]);

+				throw new IllegalArgumentException("Invoke method [" + mn + "] argument number error.");

+			}

+			throw new NoSuchMethodException("Method [" + mn + "] not found.");

+		}

+	};

+

+	/**

+	 * get wrapper.

+	 * 

+	 * @param c Class instance.

+	 * @return Wrapper instance(not null).

+	 */

+	public static Wrapper getWrapper(Class<?> c)

+    {

+        while( ClassGenerator.isDynamicClass(c) ) // can not wrapper on dynamic class.

+            c = c.getSuperclass();

+

+        if( c == Object.class )

+            return OBJECT_WRAPPER;

+

+        Wrapper ret = WRAPPER_MAP.get(c);

+        if( ret == null )

+        {

+            ret = makeWrapper(c);

+            WRAPPER_MAP.put(c,ret);

+        }

+        return ret;

+    }

+	/**

+	 * get property name array.

+	 * 

+	 * @return property name array.

+	 */

+	abstract public String[] getPropertyNames();

+

+	/**

+	 * get property type.

+	 * 

+	 * @param pn property name.

+	 * @return Property type or nul.

+	 */

+	abstract public Class<?> getPropertyType(String pn);

+

+	/**

+	 * has property.

+	 * 

+	 * @param name property name.

+	 * @return has or has not.

+	 */

+	abstract public boolean hasProperty(String name);

+

+	/**

+	 * get property value.

+	 * 

+	 * @param instance instance.

+	 * @param pn property name.

+	 * @return value.

+	 */

+	abstract public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException, IllegalArgumentException;

+

+	/**

+	 * set property value.

+	 * 

+	 * @param instance instance.

+	 * @param pn property name.

+	 * @param pv property value.

+	 */

+	abstract public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException, IllegalArgumentException;

+

+	/**

+	 * get property value.

+	 * 

+	 * @param instance instance.

+	 * @param pns property name array.

+	 * @return value array.

+	 */

+	public Object[] getPropertyValues(Object instance, String[] pns) throws NoSuchPropertyException, IllegalArgumentException

+	{

+		Object[] ret = new Object[pns.length];

+		for(int i=0;i<ret.length;i++)

+			ret[i] = getPropertyValue(instance, pns[i]);

+		return ret;

+	}

+

+	/**

+	 * set property value.

+	 * 

+	 * @param instance instance.

+	 * @param pns property name array.

+	 * @param pvs property value array.

+	 */

+	public void setPropertyValues(Object instance, String[] pns, Object[] pvs) throws NoSuchPropertyException, IllegalArgumentException

+	{

+		if( pns.length != pvs.length )

+			throw new IllegalArgumentException("pns.length != pvs.length");

+

+		for(int i=0;i<pns.length;i++)

+			setPropertyValue(instance, pns[i], pvs[i]);

+	}

+

+	/**

+	 * get method name array.

+	 * 

+	 * @return method name array.

+	 */

+	abstract public String[] getMethodNames();

+

+	/**

+	 * get method name array.

+	 * 

+	 * @return method name array.

+	 */

+	abstract public String[] getDeclaredMethodNames();

+

+	/**

+	 * has method.

+	 * 

+	 * @param name method name.

+	 * @return has or has not.

+	 */

+	public boolean hasMethod(String name)

+	{

+		for( String mn : getMethodNames() )

+			if( mn.equals(name) ) return true;

+		return false;

+	}

+

+	/**

+	 * invoke method.

+	 * 

+	 * @param instance instance.

+	 * @param mn method name.

+	 * @param types 

+	 * @param args argument array.

+	 * @return return value.

+	 */

+	abstract public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException, InvocationTargetException;

+

+	private static Wrapper makeWrapper(Class<?> c)

+	{

+		if( c.isPrimitive() )

+			throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);

+

+		String name = c.getName();

+		ClassLoader cl = ClassHelper.getClassLoader(c);

+

+		StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");

+		StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");

+		StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");

+

+		c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");

+		c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");

+		c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); } try{");

+

+		Map<String, Class<?>> pts = new HashMap<String, Class<?>>(); // <property name, property types>

+		Map<String, Method> ms = new LinkedHashMap<String, Method>(); // <method desc, Method instance>

+		List<String> mns = new ArrayList<String>(); // method names.

+		List<String> dmns = new ArrayList<String>(); // declaring method names.

+		

+		// get all public field.

+		for( Field f : c.getFields() )

+		{

+			String fn = f.getName();

+			Class<?> ft = f.getType();

+			if( Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers()) )

+				continue;

+

+			c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");

+			c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");

+			pts.put(fn, ft);

+		}

+		

+		Method[] methods = c.getMethods();

+		// get all public method.

+		for( Method m : methods )

+		{

+			if( m.getDeclaringClass() == Object.class ) //ignore Object's method.

+				continue;

+

+			String mn = m.getName();

+			c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");

+            int len = m.getParameterTypes().length;

+            c3.append(" && ").append(" $3.length == ").append(len);

+			

+			boolean override = false;

+			for( Method m2 : methods ) {

+				if (m != m2 && m.getName().equals(m2.getName())) {

+					override = true;

+					break;

+				}

+			}

+			if (override) {

+				if (len > 0) {

+					for (int l = 0; l < len; l ++) {

+						c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")

+							.append(m.getParameterTypes()[l].getName()).append("\")");

+					}

+				}

+			}

+			

+			c3.append(" ) { ");

+			

+			if( m.getReturnType() == Void.TYPE )

+				c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");

+			else

+				c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");

+

+			c3.append(" }");

+			

+			mns.add(mn);

+			if( m.getDeclaringClass() == c )

+				dmns.add(mn);

+			ms.put(ReflectUtils.getDesc(m), m);

+		}

+		c3.append(" } catch(Throwable e) { " );

+		c3.append("     throw new java.lang.reflect.InvocationTargetException(e); " );

+		c3.append(" }");

+		c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");

+		

+		// deal with get/set method.

+		Matcher matcher;

+		for( Map.Entry<String,Method> entry : ms.entrySet() )

+		{

+			String md = entry.getKey();

+			Method method = (Method)entry.getValue();

+			if( ( matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )

+			{

+				String pn = propertyName(matcher.group(1));

+				c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");

+				pts.put(pn, method.getReturnType());

+			}

+			else if( ( matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md) ).matches() )

+			{

+				String pn = propertyName(matcher.group(1));

+				c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");

+				pts.put(pn, method.getReturnType());

+			}

+			else if( ( matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )

+			{

+				Class<?> pt = method.getParameterTypes()[0];

+				String pn = propertyName(matcher.group(1));

+				c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt,"$3")).append("); return; }");

+				pts.put(pn, pt);

+			}

+		}

+		c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");

+		c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");

+

+		// make class

+		long id = WRAPPER_CLASS_COUNTER.getAndIncrement();

+		ClassGenerator cc = ClassGenerator.newInstance(cl);

+		cc.setClassName( ( Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw" ) + id );

+		cc.setSuperClass(Wrapper.class);

+

+		cc.addDefaultConstructor();

+		cc.addField("public static String[] pns;"); // property name array.

+		cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.

+		cc.addField("public static String[] mns;"); // all method name array.

+		cc.addField("public static String[] dmns;"); // declared method name array.

+		for(int i=0,len=ms.size();i<len;i++)

+			cc.addField("public static Class[] mts" + i + ";");

+

+		cc.addMethod("public String[] getPropertyNames(){ return pns; }");

+		cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");

+		cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");

+		cc.addMethod("public String[] getMethodNames(){ return mns; }");

+		cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");

+		cc.addMethod(c1.toString());

+		cc.addMethod(c2.toString());

+		cc.addMethod(c3.toString());

+

+		try

+		{

+			Class<?> wc = cc.toClass();

+			// setup static field.

+			wc.getField("pts").set(null, pts);

+			wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));

+			wc.getField("mns").set(null, mns.toArray(new String[0]));

+			wc.getField("dmns").set(null, dmns.toArray(new String[0]));

+			int ix = 0;

+			for( Method m : ms.values() )

+				wc.getField("mts" + ix++).set(null, m.getParameterTypes());

+			return (Wrapper)wc.newInstance();

+		}

+		catch(RuntimeException e)

+		{

+			throw e;

+		}

+		catch(Throwable e)

+		{

+			throw new RuntimeException(e.getMessage(), e);

+		}

+		finally

+		{

+			cc.release();

+			ms.clear();

+			mns.clear();

+			dmns.clear();

+		}

+	}

+

+	private static String arg(Class<?> cl, String name)

+	{

+		if( cl.isPrimitive() )

+		{

+			if( cl == Boolean.TYPE )

+				return "((Boolean)" + name + ").booleanValue()";

+			if( cl == Byte.TYPE )

+				return "((Byte)" + name + ").byteValue()";

+			if( cl == Character.TYPE )

+				return "((Character)" + name + ").charValue()";

+			if( cl == Double.TYPE )

+				return "((Number)" + name + ").doubleValue()";

+			if( cl == Float.TYPE )

+				return "((Number)" + name + ").floatValue()";

+			if( cl == Integer.TYPE )

+				return "((Number)" + name + ").intValue()";

+			if( cl == Long.TYPE )

+				return "((Number)" + name + ").longValue()";

+			if( cl == Short.TYPE )

+				return "((Number)" + name + ").shortValue()";

+			throw new RuntimeException("Unknown primitive type: " + cl.getName());

+		}

+		return "(" + ReflectUtils.getName(cl) + ")" + name;

+	}

+

+	private static String args(Class<?>[] cs,String name)

+	{

+		int len = cs.length;

+		if( len == 0 ) return "";

+		StringBuilder sb = new StringBuilder();

+		for(int i=0;i<len;i++)

+		{

+			if( i > 0 )

+				sb.append(',');

+			sb.append(arg(cs[i],name+"["+i+"]"));

+		}

+		return sb.toString();

+	}

+

+	private static String propertyName(String pn)

+	{

+		return pn.length() == 1 || Character.isLowerCase(pn.charAt(1)) ? Character.toLowerCase(pn.charAt(0)) + pn.substring(1) : pn;

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/Compiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/Compiler.java
new file mode 100644
index 0000000..a703196
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/Compiler.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.compiler;

+

+

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * Compiler. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@SPI("javassist")

+public interface Compiler {

+

+	/**

+	 * Compile java source code.

+	 * 

+	 * @param code Java source code

+	 * @param classLoader TODO

+	 * @return Compiled class

+	 */

+	Class<?> compile(String code, ClassLoader classLoader);

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AbstractCompiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AbstractCompiler.java
new file mode 100644
index 0000000..c926c2d
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AbstractCompiler.java
@@ -0,0 +1,69 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.compiler.support;

+

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+import com.alibaba.dubbo.common.compiler.Compiler;

+

+/**

+ * Abstract compiler. (SPI, Prototype, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractCompiler implements Compiler {

+    

+    private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([_a-zA-Z][_a-zA-Z0-9\\.]*);");

+    

+    private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([_a-zA-Z][_a-zA-Z0-9]*)\\s+");

+    

+    public Class<?> compile(String code, ClassLoader classLoader) {

+        code = code.trim();

+        Matcher matcher = PACKAGE_PATTERN.matcher(code);

+        String pkg;

+        if (matcher.find()) {

+            pkg = matcher.group(1);

+        } else {

+            pkg = "";

+        }

+        matcher = CLASS_PATTERN.matcher(code);

+        String cls;

+        if (matcher.find()) {

+            cls = matcher.group(1);

+        } else {

+            throw new IllegalArgumentException("No such class name in " + code);

+        }

+        String className = pkg != null && pkg.length() > 0 ? pkg + "." + cls : cls;

+        try {

+            return Class.forName(className, true, Thread.currentThread().getContextClassLoader());

+        } catch (ClassNotFoundException e) {

+            if (! code.endsWith("}")) {

+                throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n");

+            }

+            try {

+                return doCompile(className, code);

+            } catch (RuntimeException t) {

+                throw t;

+            } catch (Throwable t) {

+                throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(t));

+            }

+        }

+    }

+    

+    protected abstract Class<?> doCompile(String name, String source) throws Throwable;

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AdaptiveCompiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AdaptiveCompiler.java
new file mode 100644
index 0000000..77b2bc7
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AdaptiveCompiler.java
@@ -0,0 +1,49 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.compiler.support;

+

+

+import com.alibaba.dubbo.common.compiler.Compiler;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+

+/**

+ * AdaptiveCompiler. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@Adaptive

+public class AdaptiveCompiler implements Compiler {

+

+    private static volatile String DEFAULT_COMPILER;

+

+    public static void setDefaultCompiler(String compiler) {

+        DEFAULT_COMPILER = compiler;

+    }

+

+    public Class<?> compile(String code, ClassLoader classLoader) {

+        Compiler compiler;

+        ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);

+        String name = DEFAULT_COMPILER; // copy reference

+        if (name != null && name.length() > 0) {

+            compiler = loader.getExtension(name);

+        } else {

+            compiler = loader.getDefaultExtension();

+        }

+        return compiler.compile(code, classLoader);

+    }

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/ClassUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/ClassUtils.java
new file mode 100644
index 0000000..c87e595
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/ClassUtils.java
@@ -0,0 +1,417 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.compiler.support;

+

+import java.io.PrintWriter;

+import java.io.StringWriter;

+import java.lang.reflect.Array;

+import java.lang.reflect.GenericArrayType;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.lang.reflect.ParameterizedType;

+import java.net.URI;

+import java.net.URISyntaxException;

+import java.util.Collection;

+import java.util.HashMap;

+import java.util.Map;

+

+/**

+ * ClassUtils. (Tool, Static, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class ClassUtils {

+    

+    public static final String CLASS_EXTENSION = ".class";

+

+    public static final String JAVA_EXTENSION = ".java";

+

+    public static Object newInstance(String name) {

+        try {

+            return forName(name).newInstance();

+        } catch (InstantiationException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        } catch (IllegalAccessException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+    

+    public static Class<?> forName(String[] packages, String className)  {

+        try {

+            return _forName(className);

+        } catch (ClassNotFoundException e) {

+            if (packages != null && packages.length > 0) {

+                for (String pkg : packages) {

+                    try {

+                        return _forName(pkg + "." + className);

+                    } catch (ClassNotFoundException e2) {

+                    }

+                }

+            }

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+    

+    public static Class<?> forName(String className) {

+        try {

+            return _forName(className);

+        } catch (ClassNotFoundException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+    

+    public static Class<?> _forName(String className) throws ClassNotFoundException {

+        if ("boolean".equals(className))

+            return boolean.class;

+        if ("byte".equals(className))

+            return byte.class;

+        if ("char".equals(className))

+            return char.class;

+        if ("short".equals(className))

+            return short.class;

+        if ("int".equals(className))

+            return int.class;

+        if ("long".equals(className))

+            return long.class;

+        if ("float".equals(className))

+            return float.class;

+        if ("double".equals(className))

+            return double.class;

+        if ("boolean[]".equals(className))

+            return boolean[].class;

+        if ("byte[]".equals(className))

+            return byte[].class;

+        if ("char[]".equals(className))

+            return char[].class;

+        if ("short[]".equals(className))

+            return short[].class;

+        if ("int[]".equals(className))

+            return int[].class;

+        if ("long[]".equals(className))

+            return long[].class;

+        if ("float[]".equals(className))

+            return float[].class;

+        if ("double[]".equals(className))

+            return double[].class;

+        try {

+            return arrayForName(className);

+        } catch (ClassNotFoundException e) {

+            if (className.indexOf('.') == -1) { // 尝试java.lang包

+                try {

+                    return arrayForName("java.lang." + className);

+                } catch (ClassNotFoundException e2) {

+                    // 忽略尝试异常, 抛出原始异常

+                }

+            }

+            throw e;

+        }

+    }

+    

+    private static Class<?> arrayForName(String className) throws ClassNotFoundException {

+        return Class.forName(className.endsWith("[]")

+                ? "[L" + className.substring(0, className.length() - 2) + ";"

+                        : className, true, Thread.currentThread().getContextClassLoader());

+    }

+    

+    public static Class<?> getBoxedClass(Class<?> type) {

+        if (type == boolean.class) {

+            return Boolean.class; 

+        } else if (type == char.class) {

+            return Character.class; 

+        } else if (type == byte.class) {

+            return Byte.class; 

+        } else if (type == short.class) {

+            return Short.class; 

+        } else if (type == int.class) {

+            return Integer.class; 

+        } else if (type == long.class) {

+            return Long.class; 

+        } else if (type == float.class) {

+            return Float.class; 

+        } else if (type == double.class) {

+            return Double.class; 

+        } else {

+            return type;

+        }

+    }

+    

+    public static Boolean boxed(boolean v) {

+        return Boolean.valueOf(v);

+    }

+

+    public static Character boxed(char v) {

+        return Character.valueOf(v);

+    }

+

+    public static Byte boxed(byte v) {

+        return Byte.valueOf(v);

+    }

+

+    public static Short boxed(short v) {

+        return Short.valueOf(v);

+    }

+

+    public static Integer boxed(int v) {

+        return Integer.valueOf(v);

+    }

+

+    public static Long boxed(long v) {

+        return Long.valueOf(v);

+    }

+

+    public static Float boxed(float v) {

+        return Float.valueOf(v);

+    }

+

+    public static Double boxed(double v) {

+        return Double.valueOf(v);

+    }

+    

+    public static Object boxed(Object v) {

+        return v;

+    }

+    

+    public static boolean unboxed(Boolean v) {

+        return v == null ? false : v.booleanValue();

+    }

+

+    public static char unboxed(Character v) {

+        return v == null ? '\0' : v.charValue();

+    }

+

+    public static byte unboxed(Byte v) {

+        return v == null ? 0 : v.byteValue();

+    }

+

+    public static short unboxed(Short v) {

+        return v == null ? 0 : v.shortValue();

+    }

+

+    public static int unboxed(Integer v) {

+        return v == null ? 0 : v.intValue();

+    }

+

+    public static long unboxed(Long v) {

+        return v == null ? 0 : v.longValue();

+    }

+

+    public static float unboxed(Float v) {

+        return v == null ? 0 : v.floatValue();

+    }

+

+    public static double unboxed(Double v) {

+        return v == null ? 0 : v.doubleValue();

+    }

+    

+    public static Object unboxed(Object v) {

+        return v;

+    }

+    

+    public static boolean isNotEmpty(Object object) {

+        return getSize(object) > 0;

+    }

+    

+    public static int getSize(Object object) {

+        if (object == null) {

+            return 0;

+        } if (object instanceof Collection<?>) {

+            return ((Collection<?>)object).size();

+        } else if (object instanceof Map<?, ?>) {

+            return ((Map<?, ?>)object).size();

+        } else if (object.getClass().isArray()) {

+            return Array.getLength(object);

+        } else {

+            return -1;

+        }

+    }

+

+    public static URI toURI(String name) {

+        try {

+            return new URI(name);

+        } catch (URISyntaxException e) {

+            throw new RuntimeException(e);

+        }

+    }

+    

+    public static Class<?> getGenericClass(Class<?> cls) {

+        return getGenericClass(cls, 0);

+    }

+

+    public static Class<?> getGenericClass(Class<?> cls, int i) {

+        try {

+            ParameterizedType parameterizedType = ((ParameterizedType) cls.getGenericInterfaces()[0]);

+            Object genericClass = parameterizedType.getActualTypeArguments()[i];

+            if (genericClass instanceof ParameterizedType) { // 处理多级泛型

+                return (Class<?>) ((ParameterizedType) genericClass).getRawType();

+            } else if (genericClass instanceof GenericArrayType) { // 处理数组泛型

+                return (Class<?>) ((GenericArrayType) genericClass).getGenericComponentType();

+            } else if (genericClass != null) {

+                return (Class<?>) genericClass;

+            }

+        } catch (Throwable e) {

+        }

+        if (cls.getSuperclass() != null) {

+            return getGenericClass(cls.getSuperclass(), i);

+        } else {

+            throw new IllegalArgumentException(cls.getName() + " generic type undefined!");

+        }

+    }

+    

+    public static boolean isBeforeJava5(String javaVersion) {

+        return (javaVersion == null || javaVersion.length() == 0 || "1.0".equals(javaVersion) 

+                || "1.1".equals(javaVersion) || "1.2".equals(javaVersion) 

+                || "1.3".equals(javaVersion) || "1.4".equals(javaVersion));

+    }

+    

+    public static boolean isBeforeJava6(String javaVersion) {

+        return isBeforeJava5(javaVersion) || "1.5".equals(javaVersion);

+    }

+    

+    public static String toString(Throwable e) {

+        StringWriter w = new StringWriter();

+        PrintWriter p = new PrintWriter(w);

+        p.print(e.getClass().getName() + ": ");

+        if (e.getMessage() != null) {

+            p.print(e.getMessage() + "\n");

+        }

+        p.println();

+        try {

+            e.printStackTrace(p);

+            return w.toString();

+        } finally {

+            p.close();

+        }

+    }

+

+    private static final int JIT_LIMIT = 5 * 1024;

+    

+    public static void checkBytecode(String name, byte[] bytecode) {

+        if (bytecode.length > JIT_LIMIT) {

+            System.err.println("The template bytecode too long, may be affect the JIT compiler. template class: " + name);

+        }

+    }

+    

+    public static String getSizeMethod(Class<?> cls) {

+        try {

+            return cls.getMethod("size", new Class<?>[0]).getName() + "()";

+        } catch (NoSuchMethodException e) {

+            try {

+                return cls.getMethod("length", new Class<?>[0]).getName() + "()";

+            } catch (NoSuchMethodException e2) {

+                try {

+                    return cls.getMethod("getSize", new Class<?>[0]).getName() + "()";

+                } catch (NoSuchMethodException e3) {

+                    try {

+                        return cls.getMethod("getLength", new Class<?>[0]).getName() + "()";

+                    } catch (NoSuchMethodException e4) {

+                        return null;

+                    }

+                }

+            }

+        }

+    }

+    

+    public static String getMethodName(Method method, Class<?>[] parameterClasses, String rightCode) {

+        if (method.getParameterTypes().length > parameterClasses.length) {

+            Class<?>[] types = method.getParameterTypes();

+            StringBuilder buf = new StringBuilder(rightCode);

+            for (int i = parameterClasses.length; i < types.length; i ++) {

+                if (buf.length() > 0) {

+                    buf.append(",");

+                }

+                Class<?> type = types[i];

+                String def;

+                if (type == boolean.class) {

+                    def = "false";

+                } else if (type == char.class) {

+                    def = "\'\\0\'";

+                } else if (type == byte.class

+                        || type == short.class

+                        || type == int.class

+                        || type == long.class

+                        || type == float.class

+                        || type == double.class) {

+                    def = "0";

+                } else {

+                    def = "null";

+                }

+                buf.append(def);

+            }

+        }

+        return method.getName() + "(" + rightCode + ")";

+    }

+    

+    public static Method searchMethod(Class<?> currentClass, String name, Class<?>[] parameterTypes) throws NoSuchMethodException {

+        if (currentClass == null) {

+            throw new NoSuchMethodException("class == null");

+        }

+        try {

+            return currentClass.getMethod(name, parameterTypes);

+        } catch (NoSuchMethodException e) {

+            for (Method method : currentClass.getMethods()) {

+                if (method.getName().equals(name)

+                        && parameterTypes.length == method.getParameterTypes().length

+                        && Modifier.isPublic(method.getModifiers())) {

+                    if (parameterTypes.length > 0) {

+                        Class<?>[] types = method.getParameterTypes();

+                        boolean match = true;

+                        for (int i = 0; i < parameterTypes.length; i ++) {

+                            if (! types[i].isAssignableFrom(parameterTypes[i])) {

+                                match = false;

+                                break;

+                            }

+                        }

+                        if (! match) {

+                            continue;

+                        }

+                    }

+                    return method;

+                }

+            }

+            throw e;

+        }

+    }

+    

+    public static String getInitCode(Class<?> type) {

+        if (byte.class.equals(type)

+                || short.class.equals(type)

+                || int.class.equals(type)

+                || long.class.equals(type)

+                || float.class.equals(type)

+                || double.class.equals(type)) {

+            return "0";

+        } else if (char.class.equals(type)) {

+            return "'\\0'";

+        } else if (boolean.class.equals(type)) {

+            return "false";

+        } else {

+            return "null";

+        }

+    }

+    

+    public static <K, V> Map<K, V> toMap(Map.Entry<K, V>[] entries) {

+        Map<K, V> map = new HashMap<K, V>();

+        if (entries != null && entries.length > 0) {

+            for (Map.Entry<K, V> enrty : entries) {

+                map.put(enrty.getKey(), enrty.getValue());

+            }

+        }

+        return map;

+    }

+

+    private ClassUtils() {}

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JavassistCompiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JavassistCompiler.java
new file mode 100644
index 0000000..49909d4
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JavassistCompiler.java
@@ -0,0 +1,120 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.compiler.support;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+import javassist.ClassPool;

+import javassist.CtClass;

+import javassist.CtField;

+import javassist.CtNewConstructor;

+import javassist.CtNewMethod;

+import javassist.LoaderClassPath;

+

+/**

+ * JavassistCompiler. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class JavassistCompiler extends AbstractCompiler {

+

+    private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n");

+

+    private static final Pattern EXTENDS_PATTERN = Pattern.compile("\\s+extends\\s+([\\w\\.]+)[^\\{]*\\{\n");

+

+    private static final Pattern IMPLEMENTS_PATTERN = Pattern.compile("\\s+implements\\s+([\\w\\.]+)\\s*\\{\n");

+    

+    private static final Pattern METHODS_PATTERN = Pattern.compile("\n(private|public|protected)\\s+");

+

+    private static final Pattern FIELD_PATTERN = Pattern.compile("[^\n]+=[^\n]+;");

+

+    @Override

+    public Class<?> doCompile(String name, String source) throws Throwable {

+        int i = name.lastIndexOf('.');

+        String className = i < 0 ? name : name.substring(i + 1);

+        ClassPool pool = new ClassPool(true);

+        pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));

+        Matcher matcher = IMPORT_PATTERN.matcher(source);

+        List<String> importPackages = new ArrayList<String>();

+        Map<String, String> fullNames = new HashMap<String, String>();

+        while (matcher.find()) {

+            String pkg = matcher.group(1);

+            if (pkg.endsWith(".*")) {

+                String pkgName = pkg.substring(0, pkg.length() - 2);

+                pool.importPackage(pkgName);

+                importPackages.add(pkgName);

+            } else {

+                pool.importPackage(pkg);

+                int pi = pkg.lastIndexOf('.');

+                fullNames.put(pi < 0 ? pkg : pkg.substring(pi + 1), pkg);

+            }

+        }

+        String[] packages = importPackages.toArray(new String[0]);

+        matcher = EXTENDS_PATTERN.matcher(source);

+        CtClass cls;

+        if (matcher.find()) {

+            String extend = matcher.group(1).trim();

+            String extendClass;

+            if (extend.contains(".")) {

+                extendClass = extend;

+            } else if (fullNames.containsKey(extend)) {

+                extendClass = fullNames.get(extend);

+            } else {

+                extendClass = ClassUtils.forName(packages, extend).getName();

+            }

+            cls = pool.makeClass(name, pool.get(extendClass));

+        } else {

+            cls = pool.makeClass(name);

+        }

+        matcher = IMPLEMENTS_PATTERN.matcher(source);

+        if (matcher.find()) {

+            String[] ifaces = matcher.group(1).trim().split("\\,");

+            for (String iface : ifaces) {

+                iface = iface.trim();

+                String ifaceClass;

+                if (iface.contains(".")) {

+                    ifaceClass = iface;

+                } else if (fullNames.containsKey(iface)) {

+                    ifaceClass = fullNames.get(iface);

+                } else {

+                    ifaceClass = ClassUtils.forName(packages, iface).getName();

+                }

+                cls.addInterface(pool.get(ifaceClass));

+            }

+        }

+        String body = source.substring(source.indexOf("{") + 1, source.length() - 1);

+        String[] methods = METHODS_PATTERN.split(body);

+        for (String method : methods) {

+            method = method.trim();

+            if (method.length() > 0) {

+                if (method.startsWith(className)) {

+                    cls.addConstructor(CtNewConstructor.make("public " + method, cls));

+                } else if (FIELD_PATTERN.matcher(method).matches()) {

+                    cls.addField(CtField.make("private " + method, cls));

+                } else {

+                    cls.addMethod(CtNewMethod.make("public " + method, cls));

+                }

+            }

+        }

+        return cls.toClass();

+    }

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JdkCompiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JdkCompiler.java
new file mode 100644
index 0000000..4d0a558
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JdkCompiler.java
@@ -0,0 +1,289 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.compiler.support;

+

+import java.io.ByteArrayInputStream;

+import java.io.ByteArrayOutputStream;

+import java.io.File;

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+import java.net.URI;

+import java.net.URL;

+import java.net.URLClassLoader;

+import java.security.AccessController;

+import java.security.PrivilegedAction;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.Enumeration;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+

+import javax.tools.DiagnosticCollector;

+import javax.tools.FileObject;

+import javax.tools.ForwardingJavaFileManager;

+import javax.tools.JavaCompiler;

+import javax.tools.JavaFileManager;

+import javax.tools.JavaFileObject;

+import javax.tools.JavaFileObject.Kind;

+import javax.tools.SimpleJavaFileObject;

+import javax.tools.StandardJavaFileManager;

+import javax.tools.StandardLocation;

+import javax.tools.ToolProvider;

+

+/**

+ * JdkCompiler. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class JdkCompiler extends AbstractCompiler {

+

+    private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

+

+    private final DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();

+    

+    private final ClassLoaderImpl classLoader;

+    

+    private final JavaFileManagerImpl javaFileManager;

+

+    private volatile List<String> options;

+

+    public JdkCompiler(){

+        options = new ArrayList<String>();

+        options.add("-target");

+        options.add("1.6");

+        StandardJavaFileManager manager = compiler.getStandardFileManager(diagnosticCollector, null, null);

+        final ClassLoader loader = Thread.currentThread().getContextClassLoader();

+        if (loader instanceof URLClassLoader 

+                && (! loader.getClass().getName().equals("sun.misc.Launcher$AppClassLoader"))) {

+            try {

+                URLClassLoader urlClassLoader = (URLClassLoader) loader;

+                List<File> files = new ArrayList<File>();

+                for (URL url : urlClassLoader.getURLs()) {

+                    files.add(new File(url.getFile()));

+                }

+                manager.setLocation(StandardLocation.CLASS_PATH, files);

+            } catch (IOException e) {

+                throw new IllegalStateException(e.getMessage(), e);

+            }

+        }

+        classLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoaderImpl>() {

+            public ClassLoaderImpl run() {

+                return new ClassLoaderImpl(loader);

+            }

+        });

+        javaFileManager = new JavaFileManagerImpl(manager, classLoader);

+    }

+    

+    @Override

+    public Class<?> doCompile(String name, String sourceCode) throws Throwable {

+        int i = name.lastIndexOf('.');

+        String packageName = i < 0 ? "" : name.substring(0, i);

+        String className = i < 0 ? name : name.substring(i + 1);

+        JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode);

+        javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, 

+                                        className + ClassUtils.JAVA_EXTENSION, javaFileObject);

+        Boolean result = compiler.getTask(null, javaFileManager, diagnosticCollector, options, 

+                                          null, Arrays.asList(new JavaFileObject[]{javaFileObject})).call();

+        if (result == null || ! result.booleanValue()) {

+            throw new IllegalStateException("Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector);

+        }

+        return classLoader.loadClass(name);

+    }

+    

+    private final class ClassLoaderImpl extends ClassLoader {

+        

+        private final Map<String, JavaFileObject> classes = new HashMap<String, JavaFileObject>();

+

+        ClassLoaderImpl(final ClassLoader parentClassLoader) {

+            super(parentClassLoader);

+        }

+

+        Collection<JavaFileObject> files() {

+            return Collections.unmodifiableCollection(classes.values());

+        }

+

+        @Override

+        protected Class<?> findClass(final String qualifiedClassName) throws ClassNotFoundException {

+            JavaFileObject file = classes.get(qualifiedClassName);

+            if (file != null) {

+                byte[] bytes = ((JavaFileObjectImpl) file).getByteCode();

+                return defineClass(qualifiedClassName, bytes, 0, bytes.length);

+            }

+            try {

+                return Class.forName(qualifiedClassName);

+            } catch (ClassNotFoundException nf) {

+                return super.findClass(qualifiedClassName);

+            }

+        }

+

+        void add(final String qualifiedClassName, final JavaFileObject javaFile) {

+            classes.put(qualifiedClassName, javaFile);

+        }

+

+        @Override

+        protected synchronized Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {

+            return super.loadClass(name, resolve);

+        }

+

+        @Override

+        public InputStream getResourceAsStream(final String name) {

+            if (name.endsWith(ClassUtils.CLASS_EXTENSION)) {

+                String qualifiedClassName = name.substring(0, name.length() - ClassUtils.CLASS_EXTENSION.length()).replace('/', '.');

+                JavaFileObjectImpl file = (JavaFileObjectImpl) classes.get(qualifiedClassName);

+                if (file != null) {

+                    return new ByteArrayInputStream(file.getByteCode());

+                }

+            }

+            return super.getResourceAsStream(name);

+        }

+    }

+    

+    private static final class JavaFileObjectImpl extends SimpleJavaFileObject {

+

+        private ByteArrayOutputStream bytecode;

+

+        private final CharSequence    source;

+

+        public JavaFileObjectImpl(final String baseName, final CharSequence source){

+            super(ClassUtils.toURI(baseName + ClassUtils.JAVA_EXTENSION), Kind.SOURCE);

+            this.source = source;

+        }

+

+        JavaFileObjectImpl(final String name, final Kind kind){

+            super(ClassUtils.toURI(name), kind);

+            source = null;

+        }

+

+        public JavaFileObjectImpl(URI uri, Kind kind){

+            super(uri, kind);

+            source = null;

+        }

+

+        @Override

+        public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws UnsupportedOperationException {

+            if (source == null) {

+                throw new UnsupportedOperationException("source == null");

+            }

+            return source;

+        }

+

+        @Override

+        public InputStream openInputStream() {

+            return new ByteArrayInputStream(getByteCode());

+        }

+

+        @Override

+        public OutputStream openOutputStream() {

+            return bytecode = new ByteArrayOutputStream();

+        }

+

+        public byte[] getByteCode() {

+            return bytecode.toByteArray();

+        }

+    }

+    

+    private static final class JavaFileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> {

+        

+        private final ClassLoaderImpl classLoader;

+

+        private final Map<URI, JavaFileObject> fileObjects = new HashMap<URI, JavaFileObject>();

+

+        public JavaFileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) {

+            super(fileManager);

+            this.classLoader = classLoader;

+        }

+

+        @Override

+        public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {

+            FileObject o = fileObjects.get(uri(location, packageName, relativeName));

+            if (o != null)

+                return o;

+            return super.getFileForInput(location, packageName, relativeName);

+        }

+

+        public void putFileForInput(StandardLocation location, String packageName, String relativeName, JavaFileObject file) {

+            fileObjects.put(uri(location, packageName, relativeName), file);

+        }

+

+        private URI uri(Location location, String packageName, String relativeName) {

+            return ClassUtils.toURI(location.getName() + '/' + packageName + '/' + relativeName);

+        }

+

+        @Override

+        public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, Kind kind, FileObject outputFile)

+                throws IOException {

+            JavaFileObject file = new JavaFileObjectImpl(qualifiedName, kind);

+            classLoader.add(qualifiedName, file);

+            return file;

+        }

+

+        @Override

+        public ClassLoader getClassLoader(JavaFileManager.Location location) {

+            return classLoader;

+        }

+

+        @Override

+        public String inferBinaryName(Location loc, JavaFileObject file) {

+            if (file instanceof JavaFileObjectImpl)

+                return file.getName();

+            return super.inferBinaryName(loc, file);

+        }

+

+        @Override

+        public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse)

+                throws IOException {

+            Iterable<JavaFileObject> result = super.list(location, packageName, kinds, recurse);

+

+            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();

+            List<URL> urlList = new ArrayList<URL>();

+            Enumeration<URL> e = contextClassLoader.getResources("com");

+            while (e.hasMoreElements()) {

+                urlList.add(e.nextElement());

+            }

+

+            ArrayList<JavaFileObject> files = new ArrayList<JavaFileObject>();

+

+            if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {

+                for (JavaFileObject file : fileObjects.values()) {

+                    if (file.getKind() == Kind.CLASS && file.getName().startsWith(packageName)) {

+                        files.add(file);

+                    }

+                }

+

+                files.addAll(classLoader.files());

+            } else if (location == StandardLocation.SOURCE_PATH && kinds.contains(JavaFileObject.Kind.SOURCE)) {

+                for (JavaFileObject file : fileObjects.values()) {

+                    if (file.getKind() == Kind.SOURCE && file.getName().startsWith(packageName)) {

+                        files.add(file);

+                    }

+                }

+            }

+

+            for (JavaFileObject file : result) {

+                files.add(file);

+            }

+

+            return files;

+        }

+    }

+

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Activate.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Activate.java
new file mode 100644
index 0000000..4e9c07a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Activate.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extension;
+
+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+
+/**

+ * Activate.

+ * 
+ * @author william.liangf
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Activate {
+

+    String[] group() default {};

+
+    String[] value() default {};

+

+    int order() default 0;

+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Adaptive.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Adaptive.java
new file mode 100644
index 0000000..bf57b21
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Adaptive.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extension;
+
+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+import com.alibaba.dubbo.common.URL;

+
+/**
+ * 在{@link ExtensionLoader}生成Extension的Adaptive Instance时,为{@link ExtensionLoader}提供信息。
+ * 
+ * @author ding.lid
+ * 
+ * @see ExtensionLoader
+ * @see URL
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Adaptive {
+    
+    /**
+     * 从{@link URL}的Key名,对应的Value作为要Adapt成的Extension名。
+     * <p>
+     * 如果{@link URL}这些Key都没有Value,使用 用 缺省的扩展(在接口的{@link SPI}中设定的值)。<br>
+     * 比如,<code>String[] {"key1", "key2"}</code>,表示
+     * <ol>
+     * <li>先在URL上找key1的Value作为要Adapt成的Extension名;
+     * <li>key1没有Value,则使用key2的Value作为要Adapt成的Extension名。
+     * <li>key2没有Value,使用缺省的扩展。
+     * <li>如果没有设定缺省扩展,则方法调用会抛出{@link IllegalStateException}。
+     * </ol>
+     * <p>
+     * 如果不设置则缺省使用Extension接口类名的点分隔小写字串。<br>
+     * 即对于Extension接口{@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}的缺省值为<code>String[] {"yyy.invoker.wrapper"}</code>
+     * 
+     * @see SPI#value()
+     */
+    String[] value() default {};

+    

+    String method() default "";
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionFactory.java
new file mode 100644
index 0000000..73949b9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionFactory.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.common.extension;

+

+/**

+ * ExtensionFactory

+ * 

+ * @author william.liangf

+ */

+@SPI

+public interface ExtensionFactory {

+

+    /**

+     * Get extension.

+     * 

+     * @param type object type.

+     * @param name object name.

+     * @return object instance.

+     */

+    <T> T getExtension(Class<T> type, String name);

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionLoader.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionLoader.java
new file mode 100644
index 0000000..4017601
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionLoader.java
@@ -0,0 +1,811 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extension;

+

+import java.io.BufferedReader;

+import java.io.InputStreamReader;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.Collections;

+import java.util.Enumeration;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.TreeSet;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.regex.Pattern;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.support.ActivateComparator;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.Reference;

+import com.alibaba.dubbo.common.utils.StringUtils;

+

+/**

+ * Dubbo使用的扩展点获取。<p>

+ * <ul>

+ * <li>自动注入关联扩展点。</li>

+ * <li>自动Wrap上扩展点的Wrap类。</li>

+ * <li>缺省获得的的扩展点是一个Adaptive Instance。

+ * </ul>

+ * 

+ * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider">JDK5.0的自动发现机制实现</a>

+ * 

+ * @author william.liangf

+ * @author ding.lid

+ * 

+ * @see Adaptive

+ */

+public class ExtensionLoader<T> {

+    

+    private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);

+    

+	private static final String SERVICES_DIRECTORY = "META-INF/services/";

+

+    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";

+

+    private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");

+    

+    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();

+

+    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();

+

+    private final Class<?> type;

+

+    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();

+    

+    private final Reference<Map<String, Class<?>>> cachedClasses = new Reference<Map<String,Class<?>>>();

+

+    private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();

+    

+	private final ConcurrentMap<String, Reference<Object>> cachedInstances = new ConcurrentHashMap<String, Reference<Object>>();

+	

+    private volatile Class<?> cachedAdaptiveClass = null;

+    

+	private final Reference<Object> cachedAdaptiveInstance = new Reference<Object>();

+	private volatile Throwable createAdaptiveInstanceError;

+	

+    private Set<Class<?>> cachedWrapperClasses;

+    

+    private String cachedDefaultName;

+    

+    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();

+

+    private final ExtensionFactory objectFactory;

+    

+    private static <T> boolean withExtensionAnnotation(Class<T> type) {

+        return type.isAnnotationPresent(SPI.class);

+    }

+    

+    @SuppressWarnings("unchecked")

+    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {

+        if (type == null)

+            throw new IllegalArgumentException("Extension type == null");

+        if(!withExtensionAnnotation(type)) {

+            throw new IllegalArgumentException("Extension type(" + type + 

+            		") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");

+        }

+        

+        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);

+        if (loader == null) {

+            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));

+            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);

+        }

+        return loader;

+    }

+

+    private ExtensionLoader(Class<?> type) {

+        this.type = type;

+        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());

+    }

+    

+    public String getExtensionName(T extensionInstance) {

+        return getExtensionName(extensionInstance.getClass());

+    }

+

+    public String getExtensionName(Class<?> extensionClass) {

+        return cachedNames.get(extensionClass);

+    }

+

+    /**

+     * This is equivalent to <pre>

+     *     getActivateExtension(url, key, null);

+     * </pre>

+     *

+     * @see #getActivateExtension(com.alibaba.dubbo.common.URL, String, String)

+     * @param url url

+     * @param key url parameter key which used to get extension point names

+     * @return extension list which are activated.

+     */

+    public List<T> getActivateExtension(URL url, String key) {

+        return getActivateExtension(url, key, null);

+    }

+

+    /**

+     * This is equivalent to <pre>

+     *     getActivateExtension(url, values, null);

+     * </pre>

+     *

+     * @see #getActivateExtension(com.alibaba.dubbo.common.URL, String[], String)

+     * @param url url

+     * @param values extension point names

+     * @return extension list which are activated

+     */

+    public List<T> getActivateExtension(URL url, String[] values) {

+        return getActivateExtension(url, values, null);

+    }

+

+    /**

+     * This is equivalent to <pre>

+     *     getActivateExtension(url, url.getParameter(key).split(","), null);

+     * </pre>

+     *

+     * @see #getActivateExtension(com.alibaba.dubbo.common.URL, String[], String)

+     * @param url url

+     * @param key url parameter key which used to get extension point names

+     * @param group group

+     * @return extension list which are activated.

+     */

+    public List<T> getActivateExtension(URL url, String key, String group) {

+        String value = url.getParameter(key);

+        return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);

+    }

+

+    /**

+     * Get activate extensions.

+     *

+     * @see Activate

+     * @param url url

+     * @param values extension point names

+     * @param group group

+     * @return extension list which are activated

+     */

+    public List<T> getActivateExtension(URL url, String[] values, String group) {

+        List<T> exts = new ArrayList<T>();

+        List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);

+        if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {

+            getExtensionClasses();

+            for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {

+                String name = entry.getKey();

+                Activate activate = entry.getValue();

+                if (isMatchGroup(group, activate.group())) {

+                    T ext = getExtension(name);

+                    if (! names.contains(name)

+                            && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name) 

+                            && isActive(activate, url)) {

+                        exts.add(ext);

+                    }

+                }

+            }

+        }

+        Collections.sort(exts, ActivateComparator.COMPARATOR);

+        for (String name : names) {

+            if (! name.startsWith(Constants.REMOVE_VALUE_PREFIX)) {

+                T ext = getExtension(name);

+                exts.add(ext);

+            }

+        }

+        return exts;

+    }

+    

+    private boolean isMatchGroup(String group, String[] groups) {

+        if (group == null || group.length() == 0) {

+            return true;

+        }

+        if (groups != null && groups.length > 0) {

+            for (String g : groups) {

+                if (group.equals(g)) {

+                    return true;

+                }

+            }

+        }

+        return false;

+    }

+    

+    private boolean isActive(Activate activate, URL url) {

+        String[] keys = activate.value();

+        if (keys == null || keys.length == 0) {

+            return true;

+        }

+        for (String key : keys) {

+            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {

+                String k = entry.getKey();

+                String v = entry.getValue();

+                if ((k.equals(key) || k.endsWith("." + key))

+                        && ConfigUtils.isNotEmpty(v)) {

+                    return true;

+                }

+            }

+        }

+        return false;

+    }

+    

+	@SuppressWarnings("unchecked")

+	public T getExtension(String name) {

+		if (name == null || name.length() == 0)

+		    throw new IllegalArgumentException("Extension name == null");

+		if ("true".equals(name)) {

+		    return getDefaultExtension();

+		}

+		Reference<Object> reference = cachedInstances.get(name);

+		if (reference == null) {

+		    cachedInstances.putIfAbsent(name, new Reference<Object>());

+		    reference = cachedInstances.get(name);

+		}

+		Object instance = reference.get();

+		if (instance == null) {

+		    synchronized (reference) {

+	            instance = reference.get();

+	            if (instance == null) {

+	                instance = createExtension(name);

+	                reference.set(instance);

+	            }

+	        }

+		}

+		return (T) instance;

+	}

+	

+	/**

+	 * 返回缺省的扩展,如果没有设置则返回<code>null</code>。 

+	 */

+	public T getDefaultExtension() {

+	    getExtensionClasses();

+        if(null == cachedDefaultName || cachedDefaultName.length() == 0

+                || "true".equals(cachedDefaultName)) {

+            return null;

+        }

+        return getExtension(cachedDefaultName);

+	}

+

+	public boolean hasExtension(String name) {

+	    if (name == null || name.length() == 0)

+	        throw new IllegalArgumentException("Extension name == null");

+	    try {

+	        return getExtensionClass(name) != null;

+	    } catch (Throwable t) {

+	        return false;

+	    }

+	}

+    

+	public Set<String> getSupportedExtensions() {

+        Map<String, Class<?>> clazzes = getExtensionClasses();

+        return Collections.unmodifiableSet(new TreeSet<String>(clazzes.keySet()));

+    }

+

+    public Set<String> getLoadedExtensions() {

+        return Collections.unmodifiableSet(new TreeSet<String>(cachedInstances.keySet()));

+    }

+    

+	/**

+	 * 返回缺省的扩展点名,如果没有设置缺省则返回<code>null</code>。 

+	 */

+	public String getDefaultExtensionName() {

+	    getExtensionClasses();

+	    return cachedDefaultName;

+	}

+	

+

+    @SuppressWarnings("unchecked")

+    public T getAdaptiveExtension() {

+        Object instance = cachedAdaptiveInstance.get();

+        if (instance == null) {

+            if(createAdaptiveInstanceError == null) {

+                synchronized (cachedAdaptiveInstance) {

+                    instance = cachedAdaptiveInstance.get();

+                    if (instance == null) {

+                        try {

+                            instance = createAdaptiveExtension();

+                            cachedAdaptiveInstance.set(instance);

+                        } catch (Throwable t) {

+                            createAdaptiveInstanceError = t;

+                            rethrowAsRuntime(t, "fail to create adaptive instance: ");

+                        }

+                    }

+                }

+            }

+            else {

+                rethrowAsRuntime(createAdaptiveInstanceError, "fail to create adaptive instance: ");

+            }

+        }

+        

+        return (T) instance;

+    }

+

+    private static void rethrowAsRuntime(Throwable t, String message) {

+        if(t instanceof RuntimeException)

+            throw (RuntimeException)t;

+        else

+            throw new IllegalStateException(message + t.toString(), t);

+    }

+    

+    private IllegalStateException findException(String name) {

+        for (Map.Entry<String, IllegalStateException> entry : exceptions.entrySet()) {

+            if (entry.getKey().toLowerCase().contains(name.toLowerCase())) {

+                return entry.getValue();

+            }

+        }

+        StringBuilder buf = new StringBuilder("No such extension " + type.getName() + " by name " + name + ", possible causes: ");

+        int i = 1;

+        for (Map.Entry<String, IllegalStateException> entry : exceptions.entrySet()) {

+            buf.append("\r\n(");

+            buf.append(i ++);

+            buf.append(") ");

+            buf.append(entry.getKey());

+            buf.append(":\r\n");

+            buf.append(StringUtils.toString(entry.getValue()));

+        }

+        return new IllegalStateException(buf.toString());

+    }

+

+    @SuppressWarnings("unchecked")

+    private T createExtension(String name) {

+        Class<?> clazz = getExtensionClasses().get(name);

+        if (clazz == null) {

+            throw findException(name);

+        }

+        try {

+            T instance = (T) EXTENSION_INSTANCES.get(clazz);

+            if (instance == null) {

+                EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());

+                instance = (T) EXTENSION_INSTANCES.get(clazz);

+            }

+            injectExtension(instance);

+            Set<Class<?>> wrapperClasses = cachedWrapperClasses;

+            if (wrapperClasses != null && wrapperClasses.size() > 0) {

+                for (Class<?> wrapperClass : wrapperClasses) {

+                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));

+                }

+            }

+            return instance;

+        } catch (Throwable t) {

+            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +

+                    type + ")  could not be instantiated: " + t.getMessage(), t);

+        }

+    }

+    

+    private T injectExtension(T instance) {

+        try {

+            if (objectFactory != null) {

+                for (Method method : instance.getClass().getMethods()) {

+                    if (method.getName().startsWith("set")

+                            && method.getParameterTypes().length == 1

+                            && Modifier.isPublic(method.getModifiers())) {

+                        Class<?> pt = method.getParameterTypes()[0];

+                        try {

+                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";

+                            Object object = objectFactory.getExtension(pt, property);

+                            if (object != null) {

+                                method.invoke(instance, object);

+                            }

+                        } catch (Exception e) {

+                            logger.error("fail to inject via method " + method.getName()

+                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);

+                        }

+                    }

+                }

+            }

+        } catch (Exception e) {

+            logger.error(e.getMessage(), e);

+        }

+        return instance;

+    }

+    

+	private Class<?> getExtensionClass(String name) {

+	    if (type == null)

+	        throw new IllegalArgumentException("Extension type == null");

+	    if (name == null)

+	        throw new IllegalArgumentException("Extension name == null");

+	    Class<?> clazz = getExtensionClasses().get(name);

+	    if (clazz == null)

+	        throw new IllegalStateException("No such extension \"" + name + "\" for " + type.getName() + "!");

+	    return clazz;

+	}

+	

+	private Map<String, Class<?>> getExtensionClasses() {

+        Map<String, Class<?>> classes = cachedClasses.get();

+        if (classes == null) {

+            synchronized (cachedClasses) {

+                classes = cachedClasses.get();

+                if (classes == null) {

+                    classes = loadExtensionClasses();

+                    cachedClasses.set(classes);

+                }

+            }

+        }

+        return classes;

+	}

+	

+    private Map<String, Class<?>> loadExtensionClasses() {

+        final SPI defaultAnnotation = type.getAnnotation(SPI.class);

+        if(defaultAnnotation != null) {

+            String value = defaultAnnotation.value();

+            if(value != null && (value = value.trim()).length() > 0) {

+                String[] names = NAME_SEPARATOR.split(value);

+                if(names.length > 1) {

+                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()

+                            + ": " + Arrays.toString(names));

+                }

+                if(names.length == 1) cachedDefaultName = names[0];

+            }

+        }

+        

+        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();

+        loadFile(extensionClasses, DUBBO_DIRECTORY);

+        loadFile(extensionClasses, SERVICES_DIRECTORY);

+        return extensionClasses;

+    }

+    

+    private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {

+        String fileName = dir + type.getName();

+        try {

+            Enumeration<java.net.URL> urls;

+            ClassLoader classLoader = findClassLoader();

+            if (classLoader != null) {

+                urls = classLoader.getResources(fileName);

+            } else {

+                urls = ClassLoader.getSystemResources(fileName);

+            }

+            if (urls != null) {

+                while (urls.hasMoreElements()) {

+                    java.net.URL url = urls.nextElement();

+                    try {

+                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));

+                        try {

+                            String line = null;

+                            while ((line = reader.readLine()) != null) {

+                                final int ci = line.indexOf('#');

+                                if (ci >= 0) line = line.substring(0, ci);

+                                line = line.trim();

+                                if (line.length() > 0) {

+                                    try {

+                                        String name = null;

+                                        int i = line.indexOf('=');

+                                        if (i > 0) {

+                                            name = line.substring(0, i).trim();

+                                            line = line.substring(i + 1).trim();

+                                        }

+                                        Class<?> clazz = Class.forName(line, true, classLoader);

+                                        if (! type.isAssignableFrom(clazz)) {

+                                            throw new IllegalStateException("Error when load extension class(interface: " +

+                                                    type + ", class line: " + clazz.getName() + "), class " 

+                                                    + clazz.getName() + "is not subtype of interface.");

+                                        }

+                                        if (clazz.isAnnotationPresent(Adaptive.class)) {

+                                            if(cachedAdaptiveClass == null) {

+                                                cachedAdaptiveClass = clazz;

+                                            } else if (! cachedAdaptiveClass.equals(clazz)) {

+                                                throw new IllegalStateException("More than 1 adaptive class found: "

+                                                        + cachedAdaptiveClass.getClass().getName()

+                                                        + ", " + clazz.getClass().getName());

+                                            }

+                                        } else {

+                                            try {

+                                                clazz.getConstructor(type);

+                                                Set<Class<?>> wrappers = cachedWrapperClasses;

+                                                if (wrappers == null) {

+                                                    cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();

+                                                    wrappers = cachedWrapperClasses;

+                                                }

+                                                wrappers.add(clazz);

+                                            } catch (NoSuchMethodException e) {

+                                                clazz.getConstructor();

+                                                if (name == null || name.length() == 0) {

+                                                    name = findAnnotationName(clazz);

+                                                    if (name == null || name.length() == 0) {

+                                                        if (clazz.getSimpleName().length() > type.getSimpleName().length()

+                                                                && clazz.getSimpleName().endsWith(type.getSimpleName())) {

+                                                            name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();

+                                                        } else {

+                                                            throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);

+                                                        }

+                                                    }

+                                                }

+                                                String[] names = NAME_SEPARATOR.split(name);

+                                                if (names != null && names.length > 0) {

+                                                    Activate activate = clazz.getAnnotation(Activate.class);

+                                                    if (activate != null) {

+                                                        cachedActivates.put(names[0], activate);

+                                                    }

+                                                    for (String n : names) {

+                                                        if (! cachedNames.containsKey(clazz)) {

+                                                            cachedNames.put(clazz, n);

+                                                        }

+                                                        Class<?> c = extensionClasses.get(n);

+                                                        if (c == null) {

+                                                            extensionClasses.put(n, clazz);

+                                                        } else if (c != clazz) {

+                                                            throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());

+                                                        }

+                                                    }

+                                                }

+                                            }

+                                        }

+                                    } catch (Throwable t) {

+                                        IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);

+                                        exceptions.put(line, e);

+                                    }

+                                }

+                            } // end of while read lines

+                        } finally {

+                            reader.close();

+                        }

+                    } catch (Throwable t) {

+                        logger.error("Exception when load extension class(interface: " +

+                                            type + ", class file: " + url + ") in " + url, t);

+                    }

+                } // end of while urls

+            }

+        } catch (Throwable t) {

+            logger.error("Exception when load extension class(interface: " +

+                    type + ", description file: " + fileName + ").", t);

+        }

+    }

+    

+    @SuppressWarnings("deprecation")

+    private String findAnnotationName(Class<?> clazz) {

+        com.alibaba.dubbo.common.Extension extension = clazz.getAnnotation(com.alibaba.dubbo.common.Extension.class);

+        if (extension == null) {

+            String name = clazz.getSimpleName();

+            if (name.endsWith(type.getSimpleName())) {

+                name = name.substring(0, name.length() - type.getSimpleName().length());

+            }

+            return name.toLowerCase();

+        }

+        return extension.value();

+    }

+    

+    @SuppressWarnings("unchecked")

+    private T createAdaptiveExtension() {

+        try {

+            return injectExtension((T) getAdaptiveExtensionClass().newInstance());

+        } catch (Exception e) {

+            throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);

+        }

+    }

+    

+    private Class<?> getAdaptiveExtensionClass() {

+        getExtensionClasses();

+        if (cachedAdaptiveClass != null) {

+            return cachedAdaptiveClass;

+        }

+        return cachedAdaptiveClass = createAdaptiveExtensionClass();

+    }

+    

+    private Class<?> createAdaptiveExtensionClass() {

+        String code = createAdaptiveExtensionClassCode();

+        ClassLoader classLoader = findClassLoader();

+        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();

+        return compiler.compile(code, classLoader);

+    }

+    

+    private String createAdaptiveExtensionClassCode() {

+        StringBuilder codeBuidler = new StringBuilder();

+        Method[] methods = type.getMethods();

+        boolean hasAdaptiveAnnotation = false;

+        for(Method m : methods) {

+            if(m.isAnnotationPresent(Adaptive.class)) {

+                hasAdaptiveAnnotation = true;

+                break;

+            }

+        }

+        // 完全没有Adaptive方法,则不需要生成Adaptive类

+        if(! hasAdaptiveAnnotation)

+            throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");

+        

+        codeBuidler.append("package " + type.getPackage().getName() + ";");

+        codeBuidler.append("\nimport " + ExtensionLoader.class.getPackage().getName() + ";");

+        codeBuidler.append("\npublic class " + type.getSimpleName() + "_Adpative" + " implements " + type.getCanonicalName() + " {");

+        

+        for (Method method : methods) {

+            Class<?> rt = method.getReturnType();

+            Class<?>[] pts = method.getParameterTypes();

+            Class<?>[] ets = method.getExceptionTypes();

+

+            Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);

+            StringBuilder code = new StringBuilder(512);

+            if (adaptiveAnnotation == null) {

+                code.append("throw new UnsupportedOperationException(\"method ")

+                        .append(method.toString()).append(" of interface ")

+                        .append(type.getName()).append(" is not adaptive method!\");");

+            } else {

+                int urlTypeIndex = -1;

+                for (int i = 0; i < pts.length; ++i) {

+                    if (pts[i].equals(URL.class)) {

+                        urlTypeIndex = i;

+                        break;

+                    }

+                }

+                // 有类型为URL的参数

+                if (urlTypeIndex != -1) {

+                    // Null Point check

+                    String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",

+                                    urlTypeIndex);

+                    code.append(s);

+                    

+                    s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex); 

+                    code.append(s);

+                }

+                // 参数没有URL类型

+                else {

+                    String attribMethod = null;

+                    

+                    // 找到参数的URL属性

+                    LBL_PTS:

+                    for (int i = 0; i < pts.length; ++i) {

+                        Method[] ms = pts[i].getMethods();

+                        for (Method m : ms) {

+                            String name = m.getName();

+                            if ((name.startsWith("get") || name.length() > 3)

+                                    && Modifier.isPublic(m.getModifiers())

+                                    && !Modifier.isStatic(m.getModifiers())

+                                    && m.getParameterTypes().length == 0

+                                    && m.getReturnType() == URL.class) {

+                                urlTypeIndex = i;

+                                attribMethod = name;

+                                break LBL_PTS;

+                            }

+                        }

+                    }

+                    if(attribMethod == null) {

+                        throw new IllegalStateException("fail to create adative class for interface " + type.getName()

+                        		+ ": not found url parameter or url attribute in parameters of method " + method.getName());

+                    }

+                    

+                    // Null point check

+                    String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",

+                                    urlTypeIndex, pts[urlTypeIndex].getName());

+                    code.append(s);

+                    s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",

+                                    urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);

+                    code.append(s);

+

+                    s = String.format("%s url = arg%d.%s();",URL.class.getName(), urlTypeIndex, attribMethod); 

+                    code.append(s);

+                }

+                

+                String[] value = adaptiveAnnotation.value();

+                // 没有设置Key,则使用“扩展点接口名的点分隔 作为Key

+                if(value.length == 0) {

+                    char[] charArray = type.getSimpleName().toCharArray();

+                    StringBuilder sb = new StringBuilder(128);

+                    for (int i = 0; i < charArray.length; i++) {

+                        if(Character.isUpperCase(charArray[i])) {

+                            if(i != 0) {

+                                sb.append(".");

+                            }

+                            sb.append(Character.toLowerCase(charArray[i]));

+                        }

+                        else {

+                            sb.append(charArray[i]);

+                        }

+                    }

+                    value = new String[] {sb.toString()};

+                }

+                

+                String m = adaptiveAnnotation.method();

+                

+                String defaultExtName = cachedDefaultName;

+                String getNameCode = null;

+                for (int i = value.length - 1; i >= 0; --i) {

+                    if(i == value.length - 1) {

+                        if(null != defaultExtName) {

+                            if(!"protocol".equals(value[i]))

+                                if (m != null && m.length() > 0) 

+                                    getNameCode = String.format("url.getMethodParameter(arg%s, \"%s\", \"%s\")", m, value[i], defaultExtName);

+                                else

+                                    getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);

+                            else

+                                getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);

+                        }

+                        else {

+                            if(!"protocol".equals(value[i]))

+                                if (m != null && m.length() > 0) 

+                                    getNameCode = String.format("url.getMethodParameter(arg%s, \"%s\", \"%s\")", m, value[i], defaultExtName);

+                                else

+                                    getNameCode = String.format("url.getParameter(\"%s\")", value[i]);

+                            else

+                                getNameCode = "url.getProtocol()";

+                        }

+                    }

+                    else {

+                        if(!"protocol".equals(value[i]))

+                            if (m != null && m.length() > 0) 

+                                getNameCode = String.format("url.getMethodParameter(arg%s, \"%s\", \"%s\")", m, value[i], defaultExtName);

+                            else

+                                getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);

+                        else

+                            getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);

+                    }

+                }

+                code.append("\nString extName = ").append(getNameCode).append(";");

+                // check extName == null?

+                String s = String.format("\nif(extName == null) " +

+                		"throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",

+                        type.getName(), Arrays.toString(value));

+                code.append(s);

+                

+                s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",

+                        type.getName(), ExtensionLoader.class.getSimpleName(), type.getName());

+                code.append(s);

+                

+                // return statement

+                if (!rt.equals(void.class)) {

+                    code.append("\nreturn ");

+                }

+

+                s = String.format("extension.%s(", method.getName());

+                code.append(s);

+                for (int i = 0; i < pts.length; i++) {

+                    if (i != 0)

+                        code.append(", ");

+                    code.append("arg").append(i);

+                }

+                code.append(");");

+            }

+            

+            codeBuidler.append("\npublic " + rt.getCanonicalName() + " " + method.getName() + "(");

+            for (int i = 0; i < pts.length; i ++) {

+                if (i > 0) {

+                    codeBuidler.append(", ");

+                }

+                codeBuidler.append(pts[i].getCanonicalName());

+                codeBuidler.append(" ");

+                codeBuidler.append("arg" + i);

+            }

+            codeBuidler.append(")");

+            if (ets.length > 0) {

+                codeBuidler.append(" throws ");

+                for (int i = 0; i < ets.length; i ++) {

+                    if (i > 0) {

+                        codeBuidler.append(", ");

+                    }

+                    codeBuidler.append(pts[i].getCanonicalName());

+                }

+            }

+            codeBuidler.append(" {");

+            codeBuidler.append(code.toString());

+            codeBuidler.append("\n}");

+        }

+        codeBuidler.append("\n}");

+        if (logger.isDebugEnabled()) {

+            logger.debug(codeBuidler.toString());

+        }

+        return codeBuidler.toString();

+    }

+

+    private static ClassLoader findClassLoader() {

+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

+        if (classLoader != null) {

+            return classLoader;

+        }

+        classLoader = ExtensionLoader.class.getClassLoader();

+        return classLoader;

+    }

+    

+    @Override

+    public String toString() {

+        return this.getClass().getName() + "[" + type.getName() + "]";

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/SPI.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/SPI.java
new file mode 100644
index 0000000..b9f4515
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/SPI.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extension;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+/**

+ * 扩展点接口的标识。

+ * 

+ * @author william.liangf

+ * @author ding.lid

+ */

+@Documented

+@Retention(RetentionPolicy.RUNTIME)

+@Target({ElementType.TYPE})

+public @interface SPI {

+

+    /**

+     * 缺省扩展点名。

+     */

+	String value() default "";

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/AdaptiveExtensionFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/AdaptiveExtensionFactory.java
new file mode 100644
index 0000000..bf67a6a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/AdaptiveExtensionFactory.java
@@ -0,0 +1,55 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.common.extension.factory;

+

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.List;

+

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.ExtensionFactory;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+

+/**

+ * AdaptiveExtensionFactory

+ * 

+ * @author william.liangf

+ */

+@Adaptive

+public class AdaptiveExtensionFactory implements ExtensionFactory {

+    

+    private final List<ExtensionFactory> factories;

+    

+    public AdaptiveExtensionFactory() {

+        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);

+        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();

+        for (String name : loader.getSupportedExtensions()) {

+            list.add(loader.getExtension(name));

+        }

+        factories = Collections.unmodifiableList(list);

+    }

+

+    public <T> T getExtension(Class<T> type, String name) {

+        for (ExtensionFactory factory : factories) {

+            T extension = factory.getExtension(type, name);

+            if (extension != null) {

+                return extension;

+            }

+        }

+        return null;

+    }

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/SpiExtensionFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/SpiExtensionFactory.java
new file mode 100644
index 0000000..c960a74
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/SpiExtensionFactory.java
@@ -0,0 +1,39 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.common.extension.factory;

+

+import com.alibaba.dubbo.common.extension.ExtensionFactory;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * SpiExtensionFactory

+ * 

+ * @author william.liangf

+ */

+public class SpiExtensionFactory implements ExtensionFactory {

+

+    public <T> T getExtension(Class<T> type, String name) {

+        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {

+            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);

+            if (loader.getSupportedExtensions().size() > 0) {

+                return loader.getAdaptiveExtension();

+            }

+        }

+        return null;

+    }

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/support/ActivateComparator.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/support/ActivateComparator.java
new file mode 100644
index 0000000..5807ec7
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/support/ActivateComparator.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.common.extension.support;

+

+import java.util.Comparator;

+

+import com.alibaba.dubbo.common.extension.Activate;

+

+/**

+ * OrderComparetor

+ * 

+ * @author william.liangf

+ */

+public class ActivateComparator implements Comparator<Object> {

+    

+    public static final Comparator<Object> COMPARATOR = new ActivateComparator();

+

+    public int compare(Object o1, Object o2) {

+        if (o1 == null && o2 == null) {

+            return 0;

+        }

+        if (o1 == null) {

+            return -1;

+        }

+        if (o2 == null) {

+            return 1;

+        }

+        if (o1.equals(o2)) {

+            return 0;

+        }

+        Activate a1 = o1.getClass().getAnnotation(Activate.class);

+        Activate a2 = o2.getClass().getAnnotation(Activate.class);

+        int n1 = a1 == null ? 0 : a1.order();

+        int n2 = a2 == null ? 0 : a2.order();

+        return n1 > n2 ? 1 : -1; // 就算n1 == n2也不能返回0,否则在HashSet等集合中,会被认为是同一值而覆盖

+    }

+

+}

diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/Bytes.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/Bytes.java
new file mode 100644
index 0000000..c774da9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/Bytes.java
@@ -0,0 +1,970 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+import com.alibaba.dubbo.common.utils.IOUtils;
+
+/**
+ * CodecUtils.
+ * 
+ * @author qian.lei
+ */
+
+public class Bytes
+{
+	private static final String C64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; //default base64.
+
+	private static final char[] BASE16 = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}, BASE64 = C64.toCharArray();
+
+	private static final int MASK4 = 0x0f, MASK6 = 0x3f, MASK8 = 0xff;
+
+	private static final Map<Integer, byte[]> DECODE_TABLE_MAP = new ConcurrentHashMap<Integer, byte[]>();
+
+	private static ThreadLocal<MessageDigest> MD = new ThreadLocal<MessageDigest>();
+
+	/**
+	 * byte array copy.
+	 * 
+	 * @param src src.
+	 * @param length new length.
+	 * @return new byte array.
+	 */
+	public static byte[] copyOf(byte[] src, int length)
+	{
+	    byte[] dest = new byte[length];
+        System.arraycopy(src, 0, dest, 0, Math.min(src.length, length));
+        return dest;
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @return byte[].
+	 */
+	public static byte[] short2bytes(short v)
+	{
+		byte[] ret = { 0, 0 };
+		short2bytes(v, ret);
+		return ret;
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 */
+	public static void short2bytes(short v, byte[] b)
+	{
+		short2bytes(v, b, 0);
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 */
+	public static void short2bytes(short v, byte[] b, int off)
+	{
+		b[off + 1] = (byte) v;
+		b[off + 0] = (byte) (v >>> 8);
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @return byte[].
+	 */
+	public static byte[] int2bytes(int v)
+	{
+		byte[] ret = { 0, 0, 0, 0 };
+		int2bytes(v, ret);
+		return ret;
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 */
+	public static void int2bytes(int v, byte[] b)
+	{
+		int2bytes(v, b, 0);
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 * @param off array offset.
+	 */
+	public static void int2bytes(int v, byte[] b, int off)
+	{
+		b[off + 3] = (byte) v;
+		b[off + 2] = (byte) (v >>> 8);
+		b[off + 1] = (byte) (v >>> 16);
+		b[off + 0] = (byte) (v >>> 24);
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @return byte[].
+	 */
+	public static byte[] float2bytes(float v)
+	{
+		byte[] ret = { 0, 0, 0, 0 };
+		float2bytes(v, ret);
+		return ret;
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 */
+	public static void float2bytes(float v, byte[] b)
+	{
+		float2bytes(v, b, 0);
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 * @param off array offset.
+	 */
+	public static void float2bytes(float v, byte[] b, int off)
+	{
+		int i = Float.floatToIntBits(v);
+		b[off + 3] = (byte) i;
+		b[off + 2] = (byte) (i >>> 8);
+		b[off + 1] = (byte) (i >>> 16);
+		b[off + 0] = (byte) (i >>> 24);
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @return byte[].
+	 */
+	public static byte[] long2bytes(long v)
+	{
+		byte[] ret = { 0, 0, 0, 0, 0, 0, 0, 0 };
+		long2bytes(v, ret);
+		return ret;
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 */
+	public static void long2bytes(long v, byte[] b)
+	{
+		long2bytes(v, b, 0);
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 * @param off array offset.
+	 */
+	public static void long2bytes(long v, byte[] b, int off)
+	{
+		b[off + 7] = (byte) v;
+		b[off + 6] = (byte) (v >>> 8);
+		b[off + 5] = (byte) (v >>> 16);
+		b[off + 4] = (byte) (v >>> 24);
+		b[off + 3] = (byte) (v >>> 32);
+		b[off + 2] = (byte) (v >>> 40);
+		b[off + 1] = (byte) (v >>> 48);
+		b[off + 0] = (byte) (v >>> 56);
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @return byte[].
+	 */
+	public static byte[] double2bytes(double v)
+	{
+		byte[] ret = { 0, 0, 0, 0, 0, 0, 0, 0 };
+		double2bytes(v, ret);
+		return ret;
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 */
+	public static void double2bytes(double v, byte[] b)
+	{
+		double2bytes(v, b, 0);
+	}
+
+	/**
+	 * to byte array.
+	 * 
+	 * @param v value.
+	 * @param b byte array.
+	 * @param off array offset.
+	 */
+	public static void double2bytes(double v, byte[] b, int off)
+	{
+		long j = Double.doubleToLongBits(v);
+		b[off + 7] = (byte) j;
+		b[off + 6] = (byte) (j >>> 8);
+		b[off + 5] = (byte) (j >>> 16);
+		b[off + 4] = (byte) (j >>> 24);
+		b[off + 3] = (byte) (j >>> 32);
+		b[off + 2] = (byte) (j >>> 40);
+		b[off + 1] = (byte) (j >>> 48);
+		b[off + 0] = (byte) (j >>> 56);
+	}
+
+	/**
+	 * to short.
+	 * 
+	 * @param b byte array.
+	 * @return short.
+	 */
+	public static short bytes2short(byte[] b)
+	{
+		return bytes2short(b, 0);
+	}
+
+	/**
+	 * to short.
+	 * 
+	 * @param b byte array.
+	 * @param off offset.
+	 * @return short.
+	 */
+	public static short bytes2short(byte[] b, int off)
+	{
+		return (short) (((b[off + 1] & 0xFF) << 0) +
+			((b[off + 0]) << 8));
+	}
+
+	/**
+	 * to int.
+	 * 
+	 * @param b byte array.
+	 * @return int.
+	 */
+	public static int bytes2int(byte[] b)
+	{
+		return bytes2int(b, 0);
+	}
+
+	/**
+	 * to int.
+	 * 
+	 * @param b byte array.
+	 * @param off offset.
+	 * @return int.
+	 */
+	public static int bytes2int(byte[] b, int off)
+	{
+		return ((b[off + 3] & 0xFF) << 0) +
+	       ((b[off + 2] & 0xFF) << 8) +
+	       ((b[off + 1] & 0xFF) << 16) +
+	       ((b[off + 0]) << 24);
+	}
+
+	/**
+	 * to int.
+	 * 
+	 * @param b byte array.
+	 * @return int.
+	 */
+	public static float bytes2float(byte[] b)
+	{
+		return bytes2float(b, 0);
+	}
+
+	/**
+	 * to int.
+	 * 
+	 * @param b byte array.
+	 * @param off offset.
+	 * @return int.
+	 */
+	public static float bytes2float(byte[] b, int off)
+	{
+		int i = ((b[off + 3] & 0xFF) << 0) +
+			((b[off + 2] & 0xFF) << 8) +
+			((b[off + 1] & 0xFF) << 16) +
+			((b[off + 0]) << 24);
+		return Float.intBitsToFloat(i);
+	}
+
+	/**
+	 * to long.
+	 * 
+	 * @param b byte array.
+	 * @return long.
+	 */
+	public static long bytes2long(byte[] b)
+	{
+		return bytes2long(b,0);
+	}
+
+	/**
+	 * to long.
+	 * 
+	 * @param b byte array.
+	 * @param off offset.
+	 * @return long.
+	 */
+	public static long bytes2long(byte[] b,int off)
+	{
+		return ((b[off + 7] & 0xFFL) << 0) +
+	       ((b[off + 6] & 0xFFL) << 8) +
+	       ((b[off + 5] & 0xFFL) << 16) +
+	       ((b[off + 4] & 0xFFL) << 24) +
+	       ((b[off + 3] & 0xFFL) << 32) +
+	       ((b[off + 2] & 0xFFL) << 40) +
+	       ((b[off + 1] & 0xFFL) << 48) +
+	       (((long) b[off + 0]) << 56);
+	}
+
+	/**
+	 * to long.
+	 * 
+	 * @param b byte array.
+	 * @return double.
+	 */
+	public static double bytes2double(byte[] b)
+	{
+		return bytes2double(b,0);
+	}
+
+	/**
+	 * to long.
+	 * 
+	 * @param b byte array.
+	 * @param off offset.
+	 * @return double.
+	 */
+	public static double bytes2double(byte[] b, int off)
+	{
+		long j = ((b[off + 7] & 0xFFL) << 0) +
+			 ((b[off + 6] & 0xFFL) << 8) +
+			 ((b[off + 5] & 0xFFL) << 16) +
+			 ((b[off + 4] & 0xFFL) << 24) +
+			 ((b[off + 3] & 0xFFL) << 32) +
+			 ((b[off + 2] & 0xFFL) << 40) +
+			 ((b[off + 1] & 0xFFL) << 48) +
+			 (((long) b[off + 0]) << 56);
+		return Double.longBitsToDouble(j);
+	}
+
+	/**
+	 * to hex string.
+	 * 
+	 * @param bs byte array.
+	 * @return hex string.
+	 */
+	public static String bytes2hex(byte[] bs)
+	{
+		return bytes2hex(bs, 0, bs.length);
+	}
+
+	/**
+	 * to hex string.
+	 * 
+	 * @param bs byte array.
+	 * @param off offset.
+	 * @param len length.
+	 * @return hex string.
+	 */
+	public static String bytes2hex(byte[] bs, int off, int len)
+	{
+		if( off < 0 )
+			throw new IndexOutOfBoundsException("bytes2hex: offset < 0, offset is " + off );
+		if( len < 0 )
+			throw new IndexOutOfBoundsException("bytes2hex: length < 0, length is " + len );
+		if( off + len > bs.length )
+			throw new IndexOutOfBoundsException("bytes2hex: offset + length > array length.");
+
+		byte b;
+		int r = off, w = 0;
+		char[] cs = new char[len*2];
+		for(int i=0;i<len;i++)
+		{
+			b = bs[r++];
+			cs[w++] = BASE16[ b >> 4 & MASK4 ];
+			cs[w++] = BASE16[ b & MASK4 ];
+		}
+		return new String(cs);
+	}
+
+	/**
+	 * from hex string.
+	 * 
+	 * @param str hex string.
+	 * @return byte array.
+	 */
+	public static byte[] hex2bytes(String str)
+	{
+		return hex2bytes(str, 0, str.length());
+	}
+
+	/**
+	 * from hex string.
+	 * 
+	 * @param str hex string.
+	 * @param off offset.
+	 * @param len length.
+	 * @return byte array.
+	 */
+	public static byte[] hex2bytes(final String str, final int off, int len)
+	{
+		if( ( len & 1 ) == 1 )
+			throw new IllegalArgumentException("hex2bytes: ( len & 1 ) == 1.");
+
+		if( off < 0 )
+			throw new IndexOutOfBoundsException("hex2bytes: offset < 0, offset is " + off );
+		if( len < 0 )
+			throw new IndexOutOfBoundsException("hex2bytes: length < 0, length is " + len );
+		if( off + len > str.length() )
+			throw new IndexOutOfBoundsException("hex2bytes: offset + length > array length.");
+
+		int num = len / 2, r = off, w = 0;
+		byte[] b = new byte[num];
+		for(int i=0;i<num;i++)
+			b[w++] = (byte)( hex(str.charAt(r++)) << 4 | hex(str.charAt(r++)) );
+		return b;
+	}
+
+	/**
+	 * to base64 string.
+	 * 
+	 * @param b byte array.
+	 * @return base64 string.
+	 */
+	public static String bytes2base64(byte[] b)
+	{
+		return bytes2base64(b, 0, b.length, BASE64);
+	}
+
+	/**
+	 * to base64 string.
+	 * 
+	 * @param b byte array.
+	 * @return base64 string.
+	 */
+	public static String bytes2base64(byte[] b, int offset, int length)
+	{
+		return bytes2base64(b, offset, length, BASE64);
+	}
+
+	/**
+	 * to base64 string.
+	 * 
+	 * @param b byte array.
+	 * @param code base64 code string(0-63 is base64 char,64 is pad char).
+	 * @return base64 string.
+	 */
+	public static String bytes2base64(byte[] b, String code)
+	{
+		return bytes2base64(b, 0, b.length, code);
+	}
+
+	/**
+	 * to base64 string.
+	 * 
+	 * @param b byte array.
+	 * @param code base64 code string(0-63 is base64 char,64 is pad char).
+	 * @return base64 string.
+	 */
+	public static String bytes2base64(byte[] b, int offset, int length, String code)
+	{
+		if( code.length() < 64 )
+			throw new IllegalArgumentException("Base64 code length < 64.");
+
+		return bytes2base64(b, offset, length, code.toCharArray());
+	}
+
+	/**
+	 * to base64 string.
+	 * 
+	 * @param b byte array.
+	 * @param code base64 code(0-63 is base64 char,64 is pad char).
+	 * @return base64 string.
+	 */
+	public static String bytes2base64(byte[] b, char[] code)
+	{
+		return bytes2base64(b, 0, b.length, code);
+	}
+
+	/**
+	 * to base64 string.
+	 * 
+	 * @param bs byte array.
+	 * @param off offset.
+	 * @param len length.
+	 * @param code base64 code(0-63 is base64 char,64 is pad char).
+	 * @return base64 string.
+	 */
+	public static String bytes2base64(final byte[] bs, final int off, final int len, final char[] code)
+	{
+		if( off < 0 )
+			throw new IndexOutOfBoundsException("bytes2base64: offset < 0, offset is " + off );
+		if( len < 0 )
+			throw new IndexOutOfBoundsException("bytes2base64: length < 0, length is " + len );
+		if( off + len > bs.length )
+			throw new IndexOutOfBoundsException("bytes2base64: offset + length > array length.");
+
+		if( code.length < 64 )
+			throw new IllegalArgumentException("Base64 code length < 64.");
+
+		boolean pad = code.length > 64; // has pad char.
+		int num = len / 3, rem = len % 3, r = off, w = 0;
+		char[] cs = new char[ num * 4 + ( rem == 0 ? 0 : pad ? 4 : rem + 1 ) ];
+
+		for(int i=0;i<num;i++)
+		{
+			int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8, b3 = bs[r++] & MASK8;
+
+			cs[w++] = code[ b1 >> 2 ];
+			cs[w++] = code[ ( b1 << 4 ) & MASK6 | ( b2 >> 4 ) ];
+			cs[w++] = code[ ( b2 << 2 ) & MASK6 | ( b3 >> 6 ) ];
+			cs[w++] = code[ b3 & MASK6 ];
+		}
+
+		if( rem == 1 )
+		{
+			int b1 = bs[r++] & MASK8;
+			cs[w++] = code[ b1 >> 2 ];
+			cs[w++] = code[ ( b1 << 4 ) & MASK6 ];
+			if( pad )
+			{
+				cs[w++] = code[64];
+				cs[w++] = code[64];
+			}
+		}
+		else if( rem == 2 )
+		{
+			int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8;
+			cs[w++] = code[ b1 >> 2 ];
+			cs[w++] = code[ ( b1 << 4 ) & MASK6 | ( b2 >> 4 ) ];
+			cs[w++] = code[ ( b2 << 2 ) & MASK6 ];
+			if( pad )
+				cs[w++] = code[64];
+		}
+		return new String(cs);
+	}
+
+	/**
+	 * from base64 string.
+	 * 
+	 * @param str base64 string.
+	 * @return byte array.
+	 */
+	public static byte[] base642bytes(String str)
+	{
+		return base642bytes(str, 0, str.length());
+	}
+
+	/**
+	 * from base64 string.
+	 * 
+	 * @param str base64 string.
+	 * @param offset offset.
+	 * @param length length.
+	 * @return byte array.
+	 */
+	public static byte[] base642bytes(String str, int offset, int length)
+	{
+		return base642bytes(str, offset, length, C64);
+	}
+
+	/**
+	 * from base64 string.
+	 * 
+	 * @param str base64 string.
+	 * @param code base64 code(0-63 is base64 char,64 is pad char).
+	 * @return byte array.
+	 */
+	public static byte[] base642bytes(String str, String code)
+	{
+		return base642bytes(str, 0, str.length(), code);
+	}
+
+	/**
+	 * from base64 string.
+	 * 
+	 * @param str base64 string.
+	 * @param off offset.
+	 * @param len length.
+	 * @param code base64 code(0-63 is base64 char,64 is pad char).
+	 * @return byte array.
+	 */
+	public static byte[] base642bytes(final String str, final int off, final int len, final String code)
+	{
+		if( off < 0 )
+			throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off );
+		if( len < 0 )
+			throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len );
+		if( off + len > str.length() )
+			throw new IndexOutOfBoundsException("base642bytes: offset + length > string length.");
+
+		if( code.length() < 64 )
+			throw new IllegalArgumentException("Base64 code length < 64.");
+
+		int rem = len % 4;
+		if( rem == 1 )
+			throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1.");
+
+		int num = len / 4, size = num * 3;
+		if( code.length() > 64 )
+		{
+			if( rem != 0 )
+				throw new IllegalArgumentException("base642bytes: base64 string length error.");
+
+			char pc = code.charAt(64);
+			if( str.charAt(off+len-2) == pc )
+			{
+				size -= 2;
+				--num;
+				rem = 2;
+			}
+			else if( str.charAt(off+len-1) == pc )
+			{
+				size--;
+				--num;
+				rem = 3;
+			}
+		}
+		else
+		{
+			if( rem == 2 )
+				size++;
+			else if( rem == 3 )
+				size += 2;
+		}
+
+		int r = off, w = 0;
+		byte[] b = new byte[size], t = decodeTable(code);
+		for(int i=0;i<num;i++)
+		{
+			int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
+			int c3 = t[str.charAt(r++)], c4 = t[str.charAt(r++)];
+
+			b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+			b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+			b[w++] = (byte)( ( c3 << 6 ) | c4 );
+		}
+
+		if( rem == 2 )
+		{
+			int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
+
+			b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+		}
+		else if( rem == 3 )
+		{
+			int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)], c3 = t[str.charAt(r++)];
+
+			b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+			b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+		}
+		return b;
+	}
+
+	/**
+	 * from base64 string.
+	 * 
+	 * @param str base64 string.
+	 * @param code base64 code(0-63 is base64 char,64 is pad char).
+	 * @return byte array.
+	 */
+	public static byte[] base642bytes(String str, char[] code)
+	{
+		return base642bytes(str, 0, str.length(), code);
+	}
+
+	/**
+	 * from base64 string.
+	 * 
+	 * @param str base64 string.
+	 * @param off offset.
+	 * @param len length.
+	 * @param code base64 code(0-63 is base64 char,64 is pad char).
+	 * @return byte array.
+	 */
+	public static byte[] base642bytes(final String str, final int off, final int len, final char[] code)
+	{
+		if( off < 0 )
+			throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off );
+		if( len < 0 )
+			throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len );
+		if( off + len > str.length() )
+			throw new IndexOutOfBoundsException("base642bytes: offset + length > string length.");
+
+		if( code.length < 64 )
+			throw new IllegalArgumentException("Base64 code length < 64.");
+
+		int rem = len % 4;
+		if( rem == 1 )
+			throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1.");
+
+		int num = len / 4, size = num * 3;
+		if( code.length > 64 )
+		{
+			if( rem != 0 )
+				throw new IllegalArgumentException("base642bytes: base64 string length error.");
+
+			char pc = code[64];
+			if( str.charAt(off+len-2) == pc )
+				size -= 2;
+			else if( str.charAt(off+len-1) == pc )
+				size--;
+		}
+		else
+		{
+			if( rem == 2 )
+				size++;
+			else if( rem == 3 )
+				size += 2;
+		}
+
+		int r = off, w = 0;
+		byte[] b = new byte[size];
+		for(int i=0;i<num;i++)
+		{
+			int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
+			int c3 = indexOf(code, str.charAt(r++)), c4 = indexOf(code, str.charAt(r++));
+
+			b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+			b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+			b[w++] = (byte)( ( c3 << 6 ) | c4 );
+		}
+
+		if( rem == 2 )
+		{
+			int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
+
+			b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+		}
+		else if( rem == 3 )
+		{
+			int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)), c3 = indexOf(code, str.charAt(r++));
+
+			b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+			b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+		}
+		return b;
+	}
+
+	/**
+	 * zip.
+	 * 
+	 * @param bytes source.
+	 * @return compressed byte array.
+	 * @throws IOException.
+	 */
+	public static byte[] zip(byte[] bytes) throws IOException
+	{
+		UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
+		OutputStream os = new DeflaterOutputStream(bos);
+		try
+		{
+			os.write(bytes);
+		}
+		finally
+		{
+			os.close();
+			bos.close();
+		}
+		return bos.toByteArray();
+	}
+
+	/**
+	 * unzip.
+	 * 
+	 * @param bytes compressed byte array.
+	 * @return byte uncompressed array.
+	 * @throws IOException
+	 */
+	public static byte[] unzip(byte[] bytes) throws IOException
+	{
+		UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(bytes);
+		UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
+		InputStream is = new InflaterInputStream(bis);
+		try
+		{
+			IOUtils.write(is, bos);
+			return bos.toByteArray();
+		}
+		finally
+		{
+			is.close();
+			bis.close();
+			bos.close();
+		}
+	}
+
+	/**
+	 * get md5.
+	 * 
+	 * @param str input string.
+	 * @return MD5 byte array.
+	 */
+	public static byte[] getMD5(String str)
+	{
+		return getMD5(str.getBytes());
+	}
+
+	/**
+	 * get md5.
+	 * 
+	 * @param source byte array source.
+	 * @return MD5 byte array.
+	 */
+	public static byte[] getMD5(byte[] source)
+	{
+		MessageDigest md = getMessageDigest();
+		return md.digest(source);
+	}
+
+	/**
+	 * get md5.
+	 * 
+	 * @param file file source.
+	 * @return MD5 byte array.
+	 */
+	public static byte[] getMD5(File file) throws IOException
+	{
+		InputStream is = new FileInputStream(file);
+		try{ return getMD5(is); }
+		finally{ is.close(); }
+	}
+
+	/**
+	 * get md5.
+	 * 
+	 * @param is input stream.
+	 * @return MD5 byte array.
+	 */
+	public static byte[] getMD5(InputStream is) throws IOException
+	{
+		return getMD5(is, 1024 * 8);
+	}
+
+	private static byte hex(char c)
+	{
+		if( c <= '9' ) return (byte)( c - '0' );
+		if( c >= 'a' && c <= 'f' ) return (byte)( c - 'a' + 10 );
+		if( c >= 'A' && c <= 'F' ) return (byte)( c - 'A' + 10 );
+		throw new IllegalArgumentException("hex string format error [" + c + "].");
+	}
+
+	private static int indexOf(char[] cs, char c)
+	{
+		for(int i=0,len=cs.length;i<len;i++)
+			if( cs[i] == c ) return i;
+		return -1;
+	}
+
+	private static byte[] decodeTable(String code)
+	{
+		int hash = code.hashCode();
+		byte[] ret = DECODE_TABLE_MAP.get(hash);
+		if( ret == null )
+		{
+			if( code.length() < 64 )
+				throw new IllegalArgumentException("Base64 code length < 64.");
+			// create new decode table.
+			ret = new byte[128];
+			for(int i=0;i<128;i++) // init table.
+				ret[i] = -1;
+			for(int i=0;i<64;i++)
+				ret[code.charAt(i)] = (byte)i;
+			DECODE_TABLE_MAP.put(hash, ret);
+		}
+		return ret;
+	}
+
+	private static byte[] getMD5(InputStream is, int bs) throws IOException
+	{
+		MessageDigest md = getMessageDigest();
+		byte[] buf = new byte[bs];
+		while( is.available() > 0 )
+		{
+			int read, total = 0;
+			do
+			{
+				if( ( read = is.read(buf, total, bs-total) ) <= 0 )
+					break;
+				total += read;
+			}
+			while( total < bs );
+			md.update(buf);
+		}
+		return md.digest();
+	}
+
+	private static MessageDigest getMessageDigest()
+	{
+		MessageDigest ret = MD.get();
+		if( ret == null )
+		{
+			try
+			{
+				ret = MessageDigest.getInstance("MD5");
+				MD.set(ret);
+			}
+			catch(NoSuchAlgorithmException e)
+			{
+				throw new RuntimeException(e);
+			}
+		}
+		return ret;
+	}
+
+	private Bytes(){}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/ClassDescriptorMapper.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/ClassDescriptorMapper.java
new file mode 100644
index 0000000..96076bb
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/ClassDescriptorMapper.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.io;
+
+public interface ClassDescriptorMapper
+{
+	/**
+	 * get Class-Descriptor by index.
+	 * 
+	 * @param index index.
+	 * @return string.
+	 */
+	String getDescriptor(int index);
+
+	/**
+	 * get Class-Descriptor index
+	 * 
+	 * @param desc Class-Descriptor
+	 * @return index.
+	 */
+	int getDescriptorIndex(String desc);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/StreamUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/StreamUtils.java
new file mode 100644
index 0000000..d349655
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/StreamUtils.java
@@ -0,0 +1,215 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Stream utils.
+ * 
+ * @author qian.lei

+ * @author ding.lid

+ */
+
+public class StreamUtils
+{
+	private StreamUtils(){}
+
+	public static InputStream limitedInputStream(final InputStream is, final int limit) throws IOException
+	{
+		return new InputStream(){
+			private int mPosition = 0, mMark = 0, mLimit = Math.min(limit, is.available());
+
+			public int read() throws IOException
+			{
+				if( mPosition < mLimit )
+				{
+					mPosition++;
+					return is.read();
+				}
+				return -1;
+		    }
+
+			public int read(byte b[], int off, int len) throws IOException
+			{
+				if( b == null )
+				    throw new NullPointerException();
+
+				if( off < 0 || len < 0 || len > b.length - off )
+				    throw new IndexOutOfBoundsException();
+
+				if( mPosition >= mLimit )
+				    return -1;
+
+				if( mPosition + len > mLimit )
+				    len = mLimit - mPosition;
+
+				if( len <= 0 )
+				    return 0;
+
+				is.read(b, off, len);
+				mPosition += len;
+				return len;
+		    }
+
+			public long skip(long len) throws IOException
+		    {
+				if( mPosition + len > mLimit )
+					len = mLimit - mPosition;
+
+				if( len <= 0 )
+					return 0;
+
+				is.skip(len);
+				mPosition += len;
+				return len;
+		    }
+
+			public int available()
+			{
+				return mLimit - mPosition;
+			}
+
+			public boolean markSupported()
+		    {
+		    	return is.markSupported();
+			}
+
+			public void mark(int readlimit)
+			{
+				is.mark(readlimit);
+				mMark = mPosition;
+			}
+
+			public void reset() throws IOException
+			{
+				is.reset();
+				mPosition = mMark;
+			}
+
+			public void close() throws IOException
+			{}
+		};
+	}
+	
+	public static InputStream markSupportedInputStream(final InputStream is, final int markBufferSize) {
+	    if(is.markSupported()) {
+	        return is;
+	    }
+
+        return new InputStream() {
+            byte[] mMarkBuffer;
+            
+            boolean mInMarked = false;
+            boolean mInReset = false;
+            private int mPosition = 0;
+            private int mCount = 0;
+
+            boolean mDry = false;
+            
+            @Override
+            public int read() throws IOException {
+                if(!mInMarked) {
+                    return is.read();
+                }
+                else {
+                    if(mPosition < mCount) {
+                        byte b = mMarkBuffer[mPosition++];
+                        return b & 0xFF;
+                    }
+                    
+                    if(!mInReset) {
+                        if(mDry) return -1;
+                        
+                        if(null == mMarkBuffer) {
+                            mMarkBuffer = new byte[markBufferSize];
+                        }
+                        if(mPosition >= markBufferSize) {
+                            throw new IOException("Mark buffer is full!");
+                        }
+                        
+                        int read = is.read();
+                        if(-1 == read){
+                            mDry = true;
+                            return -1;
+                        }
+                        
+                        mMarkBuffer[mPosition++] = (byte) read;
+                        mCount++;
+                        
+                        return read;
+                    }
+                    else {
+                        // mark buffer is used, exit mark status!
+                        mInMarked = false;
+                        mInReset = false;
+                        mPosition = 0;
+                        mCount = 0;
+                        
+                        return is.read();
+                    }
+                }
+            }
+
+            /**
+             * NOTE: the <code>readlimit</code> argument for this class
+             *  has no meaning.
+             */
+            @Override
+            public synchronized void mark(int readlimit) {
+                mInMarked = true;
+                mInReset = false;
+                
+                // mark buffer is not empty
+                int count = mCount - mPosition;
+                if(count > 0) {
+                    System.arraycopy(mMarkBuffer, mPosition, mMarkBuffer, 0, count);
+                    mCount = count;
+                    mPosition = 0;
+                }
+            }
+            
+            @Override
+            public synchronized void reset() throws IOException {
+                if(!mInMarked) {
+                    throw new IOException("should mark befor reset!");
+                }
+                
+                mInReset = true;
+                mPosition = 0;
+            }
+            
+            @Override
+            public boolean markSupported() {
+                return true;
+            }
+            
+            @Override
+            public int available() throws IOException {
+                int available = is.available();
+                
+                if(mInMarked && mInReset) available += mCount - mPosition;
+                
+                return available;
+            }
+        };
+	}

+	
+	public static InputStream markSupportedInputStream(final InputStream is) {
+	    return markSupportedInputStream(is, 1024);
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayInputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayInputStream.java
new file mode 100644
index 0000000..248b466
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayInputStream.java
@@ -0,0 +1,118 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * UnsafeByteArrayInputStrem.
+ * 
+ * @author qian.lei
+ */
+
+public class UnsafeByteArrayInputStream extends InputStream
+{
+	protected byte mData[];
+
+	protected int mPosition, mLimit, mMark = 0;
+
+	public UnsafeByteArrayInputStream(byte buf[])
+	{
+		this(buf, 0, buf.length);
+	}
+
+	public UnsafeByteArrayInputStream(byte buf[], int offset)
+	{
+		this(buf, offset, buf.length-offset);
+    }
+
+	public UnsafeByteArrayInputStream(byte buf[], int offset, int length)
+	{
+    	mData = buf;
+    	mPosition = mMark = offset;
+        mLimit = Math.min(offset+length, buf.length);
+    }
+
+	public int read()
+	{
+		return ( mPosition < mLimit ) ? ( mData[mPosition++] & 0xff ) : -1;
+    }
+
+	public int read(byte b[], int off, int len)
+	{
+		if( b == null )
+		    throw new NullPointerException();
+		if( off < 0 || len < 0 || len > b.length - off )
+		    throw new IndexOutOfBoundsException();
+		if( mPosition >= mLimit )
+		    return -1;
+		if( mPosition + len > mLimit )
+		    len = mLimit - mPosition;
+		if( len <= 0 )
+		    return 0;
+		System.arraycopy(mData, mPosition, b, off, len);
+		mPosition += len;
+		return len;
+    }
+
+	public long skip(long len)
+    {
+		if( mPosition + len > mLimit )
+			len = mLimit - mPosition;
+		if( len <= 0 )
+			return 0;
+		mPosition += len;
+		return len;
+    }
+
+	public int available()
+	{
+		return mLimit - mPosition;
+	}
+
+	public boolean markSupported()
+    {
+    	return true;
+	}
+
+	public void mark(int readAheadLimit)
+	{
+		mMark = mPosition;
+	}
+
+	public void reset()
+	{
+		mPosition = mMark;
+	}
+
+	public void close() throws IOException
+	{}
+
+	public int position()
+	{
+		return mPosition;
+	}
+
+	public void position(int newPosition)
+	{
+		mPosition = newPosition;
+	}
+	
+	public int size() {
+		return mData == null ? 0 : mData.length;
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayOutputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayOutputStream.java
new file mode 100644
index 0000000..3243dcd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayOutputStream.java
@@ -0,0 +1,106 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+
+/**
+ * UnsafeByteArrayOutputStream.
+ * 
+ * @author qian.lei
+ */
+
+public class UnsafeByteArrayOutputStream extends OutputStream
+{
+	protected byte mBuffer[];
+
+	protected int mCount;
+
+	public UnsafeByteArrayOutputStream()
+	{
+		this(32);
+    }
+
+	public UnsafeByteArrayOutputStream(int size)
+    {
+		if( size < 0 )
+			throw new IllegalArgumentException("Negative initial size: " + size);
+		mBuffer = new byte[size];
+	}
+
+	public void write(int b)
+	{
+		int newcount = mCount + 1;
+		if( newcount > mBuffer.length )
+			mBuffer = Bytes.copyOf(mBuffer, Math.max(mBuffer.length << 1, newcount));
+		mBuffer[mCount] = (byte)b;
+		mCount = newcount;
+	}
+
+	public void write(byte b[], int off, int len)
+	{
+		if( ( off < 0 ) || ( off > b.length ) || ( len < 0 ) || ( ( off + len ) > b.length ) || ( ( off + len ) < 0 ) )
+			throw new IndexOutOfBoundsException();
+		if( len == 0 )
+			return;
+		int newcount = mCount + len;
+		if( newcount > mBuffer.length )
+			mBuffer = Bytes.copyOf(mBuffer, Math.max(mBuffer.length << 1, newcount));
+		System.arraycopy(b, off, mBuffer, mCount, len);
+		mCount = newcount;
+	}
+
+	public int size()
+	{
+		return mCount;
+	}
+
+	public void reset()
+	{
+		mCount = 0;
+	}
+
+	public byte[] toByteArray()
+	{
+		return Bytes.copyOf(mBuffer, mCount);
+	}
+
+	public ByteBuffer toByteBuffer()
+	{
+		return ByteBuffer.wrap(mBuffer, 0, mCount);
+	}
+
+	public void writeTo(OutputStream out) throws IOException
+	{
+		out.write(mBuffer, 0, mCount);
+	}
+
+	public String toString()
+	{
+		return new String(mBuffer, 0, mCount);
+	}
+
+	public String toString(String charset) throws UnsupportedEncodingException
+	{
+		return new String(mBuffer, 0, mCount, charset);
+	}
+
+	public void close() throws IOException
+	{}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringReader.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringReader.java
new file mode 100644
index 0000000..3db1b5a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringReader.java
@@ -0,0 +1,120 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Thread unsafed StringReader.
+ * 
+ * @author qian.lei
+ */
+
+public class UnsafeStringReader extends Reader
+{
+	private String mString;
+
+	private int mPosition, mLimit, mMark;
+
+	public UnsafeStringReader(String str)
+	{
+		mString = str;
+		mLimit = str.length();
+		mPosition = mMark = 0;
+	}
+
+	@Override
+	public int read() throws IOException
+	{
+		ensureOpen();
+		if( mPosition >= mLimit )
+			return -1;
+
+		return mString.charAt(mPosition++);
+	}
+
+	@Override
+	public int read(char[] cs, int off, int len) throws IOException
+	{
+		ensureOpen();
+		if( (off < 0) || (off > cs.length) || (len < 0) ||
+				((off + len) > cs.length) || ((off + len) < 0) )
+			throw new IndexOutOfBoundsException();
+
+		if( len == 0 )
+			return 0;
+
+		if( mPosition >= mLimit )
+			return -1;
+
+		int n = Math.min(mLimit - mPosition, len);
+		mString.getChars(mPosition, mPosition + n, cs, off);
+		mPosition += n;
+		return n;
+	}
+
+	public long skip(long ns) throws IOException
+	{
+		ensureOpen();
+		if( mPosition >= mLimit )
+			return 0;
+
+		long n = Math.min(mLimit - mPosition, ns);
+		n = Math.max(-mPosition, n);
+		mPosition += n;
+		return n;
+	}
+
+	public boolean ready() throws IOException
+	{
+		ensureOpen();
+		return true;
+	}
+
+	@Override
+	public boolean markSupported()
+	{
+		return true;
+	}
+
+	public void mark(int readAheadLimit) throws IOException
+	{
+		if( readAheadLimit < 0 )
+			throw new IllegalArgumentException("Read-ahead limit < 0");
+
+		ensureOpen();
+		mMark = mPosition;
+	}
+
+	public void reset() throws IOException
+	{
+		ensureOpen();
+		mPosition = mMark;
+	}
+ 
+	@Override
+	public void close() throws IOException
+	{
+		mString = null;
+	}
+
+    private void ensureOpen() throws IOException
+    {
+    	if( mString == null )
+    		throw new IOException("Stream closed");
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringWriter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringWriter.java
new file mode 100644
index 0000000..7761fe1
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringWriter.java
@@ -0,0 +1,115 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Thread unsafed StringWriter.
+ * 
+ * @author qian.lei
+ */
+
+public class UnsafeStringWriter extends Writer
+{
+	private StringBuilder mBuffer;
+
+	public UnsafeStringWriter()
+	{
+		lock = mBuffer = new StringBuilder();
+	}
+
+	public UnsafeStringWriter(int size)
+	{
+		if( size < 0 )
+		    throw new IllegalArgumentException("Negative buffer size");
+
+		lock = mBuffer = new StringBuilder();
+	}
+
+	@Override
+	public void write(int c)
+	{
+		mBuffer.append((char)c);
+	}
+
+	@Override
+	public void write(char[] cs) throws IOException
+	{
+		mBuffer.append(cs, 0, cs.length);
+	}
+
+	@Override
+	public void write(char[] cs, int off, int len) throws IOException
+	{
+		if( (off < 0) || (off > cs.length) || (len < 0) ||
+				((off + len) > cs.length) || ((off + len) < 0) )
+			throw new IndexOutOfBoundsException();
+
+		if( len > 0 )
+			mBuffer.append(cs, off, len);
+	}
+
+	@Override
+	public void write(String str)
+	{
+		mBuffer.append(str);
+	}
+
+	@Override
+	public void write(String str, int off, int len)
+	{
+		mBuffer.append(str.substring(off, off + len));
+	}
+
+	@Override
+	public Writer append(CharSequence csq)
+	{
+		if (csq == null)
+			write("null");
+		else
+			write(csq.toString());
+		return this;
+	}
+
+	@Override
+	public Writer append(CharSequence csq, int start, int end)
+	{
+		CharSequence cs = (csq == null ? "null" : csq);
+		write(cs.subSequence(start, end).toString());
+		return this;
+	}
+
+	@Override
+	public Writer append(char c)
+	{
+		mBuffer.append(c);
+		return this;
+	}
+
+	@Override
+	public void close(){}
+
+	@Override
+	public void flush(){}
+
+	@Override
+	public String toString()
+	{
+		return mBuffer.toString();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/GenericJSONConverter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/GenericJSONConverter.java
new file mode 100644
index 0000000..32149c9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/GenericJSONConverter.java
@@ -0,0 +1,470 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.io.Bytes;
+
+public class GenericJSONConverter implements JSONConverter
+{
+	private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+	
+	protected interface Encoder{ void encode(Object obj, JSONWriter jb) throws IOException; }
+
+	protected interface Decoder{ Object decode(Object jv) throws IOException; }
+
+	private static final Map<Class<?>, Encoder> GlobalEncoderMap = new HashMap<Class<?>, Encoder>();
+
+	private static final Map<Class<?>, Decoder> GlobalDecoderMap = new HashMap<Class<?>, Decoder>();
+
+	@SuppressWarnings("unchecked")
+	public void writeValue(Object obj, JSONWriter jb, boolean writeClass) throws IOException
+	{
+		if (obj == null) {
+			jb.valueNull();
+			return;
+		}
+		Class<?> c = obj.getClass();
+		Encoder encoder = GlobalEncoderMap.get(c);
+
+		if( encoder != null )
+		{
+			encoder.encode(obj, jb);
+		}
+		else if( obj instanceof JSONNode )
+		{
+			((JSONNode)obj).writeJSON(this, jb, writeClass);
+		}
+		else if( c.isEnum() )
+		{
+			jb.valueString(((Enum<?>)obj).name());
+		}
+		else if( c.isArray() )
+		{
+			int len = Array.getLength(obj);
+			jb.arrayBegin();
+			for(int i=0;i<len;i++)
+				writeValue(Array.get(obj, i), jb, writeClass);
+			jb.arrayEnd();
+		}
+		else if( Map.class.isAssignableFrom(c) )
+		{
+			Object key, value;
+			jb.objectBegin();
+			for( Map.Entry<Object, Object> entry : ((Map<Object, Object>)obj).entrySet() )
+			{
+				key = entry.getKey();
+				if( key == null )
+					continue;
+				jb.objectItem(key.toString());
+
+				value = entry.getValue();
+				if( value == null )
+					jb.valueNull();
+				else
+					writeValue(value, jb, writeClass);
+			}
+			jb.objectEnd();
+		}
+		else if( Collection.class.isAssignableFrom(c) )
+		{
+			jb.arrayBegin();
+			for( Object item : (Collection<Object>)obj )
+			{
+				if( item == null )
+					jb.valueNull();
+				else
+					writeValue(item, jb, writeClass);
+			}
+			jb.arrayEnd();
+		}
+		else
+		{
+			jb.objectBegin();
+			
+			Wrapper w = Wrapper.getWrapper(c);
+			String pns[] = w.getPropertyNames();
+
+			for( String pn : pns )
+			{
+				if ((obj instanceof Throwable) && (
+						"localizedMessage".equals(pn) 
+						|| "cause".equals(pn) 
+						|| "stackTrace".equals(pn))) {
+					continue;
+				}
+				
+				jb.objectItem(pn);
+
+				Object value = w.getPropertyValue(obj,pn);
+				if( value == null || value == obj)
+					jb.valueNull();
+				else
+					writeValue(value, jb, writeClass);
+			}
+			if (writeClass) {
+			    jb.objectItem(JSONVisitor.CLASS_PROPERTY);
+			    writeValue(obj.getClass().getName(), jb, writeClass);
+			}
+			jb.objectEnd();
+		}
+	}
+
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public Object readValue(Class<?> c, Object jv) throws IOException
+	{
+		if (jv == null) {
+			return null;
+		}
+		Decoder decoder = GlobalDecoderMap.get(c);
+		if( decoder != null ) {
+			return decoder.decode(jv);
+		}
+		if (c.isEnum()) {
+			return Enum.valueOf((Class<Enum>)c, String.valueOf(jv));
+		}
+		return jv;
+	}
+
+	static
+	{
+		// init encoder map.
+		Encoder e = new Encoder(){
+			public void encode(Object obj, JSONWriter jb) throws IOException
+			{
+				jb.valueBoolean((Boolean)obj);
+			}
+		};
+		GlobalEncoderMap.put(boolean.class, e);
+		GlobalEncoderMap.put(Boolean.class, e);
+
+		e = new Encoder(){
+			public void encode(Object obj, JSONWriter jb) throws IOException
+			{
+				jb.valueInt(((Number)obj).intValue());
+			}
+		};
+		GlobalEncoderMap.put(int.class, e);
+		GlobalEncoderMap.put(Integer.class, e);
+		GlobalEncoderMap.put(short.class, e);
+		GlobalEncoderMap.put(Short.class, e);
+		GlobalEncoderMap.put(byte.class, e);
+		GlobalEncoderMap.put(Byte.class, e);
+		GlobalEncoderMap.put(AtomicInteger.class, e);
+		
+		e = new Encoder(){
+			public void encode(Object obj, JSONWriter jb) throws IOException
+			{
+				jb.valueString(Character.toString((Character)obj));
+			}
+		};
+		GlobalEncoderMap.put(char.class, e);
+		GlobalEncoderMap.put(Character.class, e);
+		
+		e = new Encoder(){
+			public void encode(Object obj, JSONWriter jb) throws IOException
+			{
+				jb.valueLong(((Number)obj).longValue());
+			}
+		};
+		GlobalEncoderMap.put(long.class, e);
+		GlobalEncoderMap.put(Long.class, e);
+		GlobalEncoderMap.put(AtomicLong.class, e);
+		GlobalEncoderMap.put(BigInteger.class, e);
+
+		e = new Encoder(){
+			public void encode(Object obj, JSONWriter jb) throws IOException
+			{
+				jb.valueFloat(((Number)obj).floatValue());
+			}
+		};
+		GlobalEncoderMap.put(float.class, e);
+		GlobalEncoderMap.put(Float.class, e);
+
+		e = new Encoder(){
+			public void encode(Object obj, JSONWriter jb) throws IOException
+			{
+				jb.valueDouble(((Number)obj).doubleValue());
+			}
+		};
+		GlobalEncoderMap.put(double.class, e);
+		GlobalEncoderMap.put(Double.class, e);
+		GlobalEncoderMap.put(BigDecimal.class, e);
+
+		e = new Encoder(){
+			public void encode(Object obj, JSONWriter jb) throws IOException
+			{
+				jb.valueString(obj.toString());
+			}
+		};
+		GlobalEncoderMap.put(String.class, e);
+		GlobalEncoderMap.put(StringBuilder.class, e);
+		GlobalEncoderMap.put(StringBuffer.class, e);
+
+		e = new Encoder(){
+			public void encode(Object obj, JSONWriter jb) throws IOException
+			{
+				jb.valueString(Bytes.bytes2base64((byte[])obj));
+			}
+		};
+		GlobalEncoderMap.put(byte[].class, e);
+		
+		e = new Encoder(){
+			public void encode(Object obj, JSONWriter jb) throws IOException
+			{
+				jb.valueString(new SimpleDateFormat(DATE_FORMAT).format((Date)obj));
+			}
+		};
+		GlobalEncoderMap.put(Date.class, e);
+
+		// init decoder map.
+		Decoder d = new Decoder(){
+			public Object decode(Object jv){ 
+				return jv.toString(); 
+			} 
+		};
+		GlobalDecoderMap.put(String.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Boolean ) return ((Boolean)jv).booleanValue();
+				return false;
+			}
+		};
+		GlobalDecoderMap.put(boolean.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Boolean ) return (Boolean)jv;
+				return (Boolean)null;
+			}
+		};
+		GlobalDecoderMap.put(Boolean.class, d);
+		
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof String && ((String)jv).length() > 0) return ((String)jv).charAt(0);
+				return (char)0;
+			}
+		};
+		GlobalDecoderMap.put(char.class, d);
+		
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof String && ((String)jv).length() > 0) return ((String)jv).charAt(0);
+				return (Character)null;
+			}
+		};
+		GlobalDecoderMap.put(Character.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return ((Number)jv).intValue();
+				return 0;
+			}
+		};
+		GlobalDecoderMap.put(int.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return Integer.valueOf(((Number)jv).intValue());
+				return (Integer)null;
+			}
+		};
+		GlobalDecoderMap.put(Integer.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return ((Number)jv).shortValue();
+				return (short)0;
+			}
+		};
+		GlobalDecoderMap.put(short.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return Short.valueOf(((Number)jv).shortValue());
+				return (Short)null;
+			}
+		};
+		GlobalDecoderMap.put(Short.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return ((Number)jv).longValue();
+				return (long)0;
+			}
+		};
+		GlobalDecoderMap.put(long.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return Long.valueOf(((Number)jv).longValue());
+				return (Long)null;
+			}
+		};
+		GlobalDecoderMap.put(Long.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return ((Number)jv).floatValue();
+				return (float)0;
+			}
+		};
+		GlobalDecoderMap.put(float.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return new Float(((Number)jv).floatValue());
+				return (Float)null;
+			}
+		};
+		GlobalDecoderMap.put(Float.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return ((Number)jv).doubleValue();
+				return (double)0;
+			}
+		};
+		GlobalDecoderMap.put(double.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return new Double(((Number)jv).doubleValue());
+				return (Double)null;
+			}
+		};
+		GlobalDecoderMap.put(Double.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return ((Number)jv).byteValue();
+				return (byte)0;
+			}
+		};
+		GlobalDecoderMap.put(byte.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv)
+			{
+				if( jv instanceof Number ) return Byte.valueOf(((Number)jv).byteValue());
+				return (Byte)null;
+			}
+		};
+		GlobalDecoderMap.put(Byte.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv) throws IOException
+			{
+				if( jv instanceof String ) return Bytes.base642bytes((String)jv);
+				return (byte[])null;
+			}
+		};
+		GlobalDecoderMap.put(byte[].class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv) throws IOException{ return new StringBuilder(jv.toString()); }
+		};
+		GlobalDecoderMap.put(StringBuilder.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv) throws IOException{ return new StringBuffer(jv.toString()); }
+		};
+		GlobalDecoderMap.put(StringBuffer.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv) throws IOException
+			{
+				if( jv instanceof Number ) return BigInteger.valueOf(((Number)jv).longValue());
+				return (BigInteger)null;
+			}
+		};
+		GlobalDecoderMap.put(BigInteger.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv) throws IOException
+			{
+				if( jv instanceof Number ) return BigDecimal.valueOf(((Number)jv).doubleValue());
+				return (BigDecimal)null;
+			}
+		};
+		GlobalDecoderMap.put(BigDecimal.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv) throws IOException
+			{
+				if( jv instanceof Number ) return new AtomicInteger(((Number)jv).intValue());
+				return (AtomicInteger)null;
+			}
+		};
+		GlobalDecoderMap.put(AtomicInteger.class, d);
+
+		d = new Decoder(){
+			public Object decode(Object jv) throws IOException
+			{
+				if( jv instanceof Number ) return new AtomicLong(((Number)jv).longValue());
+				return (AtomicLong)null;
+			}
+		};
+		GlobalDecoderMap.put(AtomicLong.class, d);
+		
+		d = new Decoder(){
+			public Object decode(Object jv) throws IOException
+			{
+				if( jv instanceof String ) {
+					try {
+						return new SimpleDateFormat(DATE_FORMAT).parse((String) jv);
+					} catch (ParseException e) {
+						throw new IllegalArgumentException(e.getMessage(), e);
+					}
+				}
+				if( jv instanceof Number ) 
+					return new Date(((Number)jv).longValue());
+				return (Date)null;
+			}
+		};
+		GlobalDecoderMap.put(Date.class, d);
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/J2oVisitor.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/J2oVisitor.java
new file mode 100644
index 0000000..132d5ef
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/J2oVisitor.java
@@ -0,0 +1,408 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+import java.io.IOException;

+import java.lang.reflect.Array;

+import java.lang.reflect.Field;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.LinkedList;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+

+import com.alibaba.dubbo.common.bytecode.Wrapper;

+import com.alibaba.dubbo.common.utils.Stack;

+import com.alibaba.dubbo.common.utils.StringUtils;

+

+/**

+ * JSON to Object visitor.

+ * 

+ * @author qian.lei.

+ */

+

+class J2oVisitor implements JSONVisitor

+{

+	public static final boolean[] EMPTY_BOOL_ARRAY = new boolean[0];

+

+	public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

+

+	public static final char[] EMPTY_CHAR_ARRAY = new char[0];

+

+	public static final short[] EMPTY_SHORT_ARRAY = new short[0];

+

+	public static final int[] EMPTY_INT_ARRAY = new int[0];

+

+	public static final long[] EMPTY_LONG_ARRAY = new long[0];

+

+	public static final float[] EMPTY_FLOAT_ARRAY = new float[0];

+

+	public static final double[] EMPTY_DOUBLE_ARRAY = new double[0];

+

+	public static final String[] EMPTY_STRING_ARRAY = new String[0];

+

+	private Class<?>[] mTypes;

+

+	private Class<?> mType = Object[].class;

+

+	private Object mValue;

+

+	private Wrapper mWrapper;

+

+	private JSONConverter mConverter;

+

+	private Stack<Object> mStack = new Stack<Object>();

+

+	J2oVisitor(Class<?> type, JSONConverter jc)

+	{

+		mType = type;

+		mConverter = jc;

+	}

+

+	J2oVisitor(Class<?>[] types, JSONConverter jc)

+	{

+		mTypes = types;

+		mConverter = jc;

+	}

+

+	public void begin()

+	{}

+

+	public Object end(Object obj, boolean isValue) throws ParseException

+	{

+		mStack.clear();

+		try {

+			return mConverter.readValue(mType, obj);

+		} catch (IOException e) {

+			throw new IllegalStateException(e.getMessage(), e);

+		}

+	}

+

+	public void objectBegin() throws ParseException

+	{

+		mStack.push(mValue);

+		mStack.push(mType);

+		mStack.push(mWrapper);

+

+		if( mType == Object.class || Map.class.isAssignableFrom(mType) )

+		{

+			if (! mType.isInterface() && mType != Object.class) {

+				try {

+					mValue = mType.newInstance();

+				} catch (Exception e) {

+					throw new IllegalStateException(e.getMessage(), e);

+				}

+			} else if (mType == ConcurrentMap.class) {

+				mValue = new ConcurrentHashMap<String, Object>();

+			} else {

+				mValue = new HashMap<String, Object>();

+			}

+			mWrapper = null;

+		} else {

+			try {

+				mValue = mType.newInstance();

+				mWrapper = Wrapper.getWrapper(mType);

+			} catch(IllegalAccessException e){ 

+				throw new ParseException(StringUtils.toString(e)); 

+			} catch(InstantiationException e){ 

+				throw new ParseException(StringUtils.toString(e)); 

+			}

+		}

+	}

+

+	public Object objectEnd(int count)

+	{

+		Object ret = mValue;

+		mWrapper = (Wrapper)mStack.pop();

+		mType = (Class<?>)mStack.pop();

+		mValue = mStack.pop();

+		return ret;

+	}

+

+	public void objectItem(String name)

+	{

+		mStack.push(name); // push name.

+		mType = ( mWrapper == null ? Object.class : mWrapper.getPropertyType(name) );

+	}

+

+	@SuppressWarnings("unchecked")

+	public void objectItemValue(Object obj, boolean isValue) throws ParseException

+	{

+		String name = (String)mStack.pop();  // pop name.

+		if( mWrapper == null )

+		{

+			((Map<String, Object>)mValue).put(name, obj);

+		}

+		else

+		{

+			if( mType != null )

+			{

+				if( isValue && obj != null )

+				{

+					try

+					{

+						obj = mConverter.readValue(mType, obj);

+					}

+					catch(IOException e)

+					{

+						throw new ParseException(StringUtils.toString(e));

+					}

+				}

+				if (mValue instanceof Throwable && "message".equals(name)) {

+					try {

+						Field field = Throwable.class.getDeclaredField("detailMessage");

+						if (! field.isAccessible()) {

+							field.setAccessible(true);

+						}

+						field.set(mValue, obj);

+					} catch (NoSuchFieldException e) {

+						throw new ParseException(StringUtils.toString(e));

+					} catch (IllegalAccessException e) {

+						throw new ParseException(StringUtils.toString(e));

+					}

+				} else if (! CLASS_PROPERTY.equals(name)) {

+					mWrapper.setPropertyValue(mValue, name, obj);

+				}

+			}

+		}

+	}

+

+	public void arrayBegin() throws ParseException

+	{

+		mStack.push(mType);

+

+		if( mType.isArray() )

+			mType = mType.getComponentType();

+		else if( mType == Object.class || Collection.class.isAssignableFrom(mType) )

+			mType = Object.class;

+		else

+			throw new ParseException("Convert error, can not load json array data into class [" + mType.getName() + "].");

+	}

+

+	@SuppressWarnings("unchecked")

+	public Object arrayEnd(int count) throws ParseException

+	{

+		Object ret;

+		mType = (Class<?>)mStack.get(-1-count);

+

+		if( mType.isArray() )

+		{

+			ret = toArray(mType.getComponentType(), mStack, count);

+		}

+		else

+		{

+			Collection<Object> items;

+			if( mType == Object.class || Collection.class.isAssignableFrom(mType)) {

+				if (! mType.isInterface() && mType != Object.class) {

+					try {

+						items = (Collection<Object>) mType.newInstance();

+					} catch (Exception e) {

+						throw new IllegalStateException(e.getMessage(), e);

+					}

+				} else if (mType.isAssignableFrom(ArrayList.class)) { // List

+					items = new ArrayList<Object>(count);

+				} else if (mType.isAssignableFrom(HashSet.class)) { // Set

+					items = new HashSet<Object>(count);

+				} else if (mType.isAssignableFrom(LinkedList.class)) { // Queue

+					items = new LinkedList<Object>();

+				} else { // Other

+					items = new ArrayList<Object>(count);

+				}

+			} else {

+				throw new ParseException("Convert error, can not load json array data into class [" + mType.getName() + "].");

+			}

+			for(int i=0;i<count;i++)

+				items.add(mStack.remove(i-count));

+			ret = items;

+		}

+		mStack.pop();

+		return ret;

+	}

+

+	public void arrayItem(int index) throws ParseException

+	{

+		if( mTypes != null && mStack.size() == index+1 )

+		{

+			if( index < mTypes.length )

+				mType = mTypes[index];

+			else

+				throw new ParseException("Can not load json array data into [" + name(mTypes) + "].");

+		}

+	}

+

+	public void arrayItemValue(int index, Object obj, boolean isValue) throws ParseException

+	{

+		if( isValue && obj != null )

+		{

+			try

+			{

+				obj = mConverter.readValue(mType, obj);

+			}

+			catch(IOException e)

+			{

+				throw new ParseException(e.getMessage());

+			}

+		}

+

+		mStack.push(obj);

+	}

+

+	private static Object toArray(Class<?> c, Stack<Object> list, int len) throws ParseException

+	{

+		if( c == String.class )

+		{

+			if( len == 0 )

+			{

+				return EMPTY_STRING_ARRAY;

+			}

+			else

+			{

+				Object o;

+				String ss[] = new String[len];

+				for(int i=len-1;i>=0;i--)

+				{

+					o = list.pop();

+					ss[i] = ( o == null ? null : o.toString() );

+				}

+				return ss;

+			}

+		}

+		if( c == boolean.class )

+		{

+			if( len == 0 ) return EMPTY_BOOL_ARRAY;

+			Object o;

+			boolean[] ret = new boolean[len];

+			for(int i=len-1;i>=0;i--)

+			{

+				o = list.pop();

+				if( o instanceof Boolean )

+					ret[i] = ((Boolean)o).booleanValue();

+			}

+			return ret;

+		}

+		if( c == int.class )

+		{

+			if( len == 0 ) return EMPTY_INT_ARRAY;

+			Object o;

+			int[] ret = new int[len];

+			for(int i=len-1;i>=0;i--)

+			{

+				o = list.pop();

+				if( o instanceof Number )

+					ret[i] = ((Number)o).intValue();

+			}

+			return ret;

+		}

+		if( c == long.class )

+		{

+			if( len == 0 ) return EMPTY_LONG_ARRAY;

+			Object o;

+			long[] ret = new long[len];

+			for(int i=len-1;i>=0;i--)

+			{

+				o = list.pop();

+				if( o instanceof Number )

+					ret[i] = ((Number)o).longValue();

+			}

+			return ret;

+		}

+		if( c == float.class )

+		{

+			if( len == 0 ) return EMPTY_FLOAT_ARRAY;

+			Object o;

+			float[] ret = new float[len];

+			for(int i=len-1;i>=0;i--)

+			{

+				o = list.pop();

+				if( o instanceof Number )

+					ret[i] = ((Number)o).floatValue();

+			}

+			return ret;

+		}

+		if( c == double.class )

+		{

+			if( len == 0 ) return EMPTY_DOUBLE_ARRAY;

+			Object o;

+			double[] ret = new double[len];

+			for(int i=len-1;i>=0;i--)

+			{

+				o = list.pop();

+				if( o instanceof Number )

+					ret[i] = ((Number)o).doubleValue();

+			}

+			return ret;

+		}

+		if( c == byte.class )

+		{

+			if( len == 0 ) return EMPTY_BYTE_ARRAY;

+			Object o;

+			byte[] ret = new byte[len];

+			for(int i=len-1;i>=0;i--)

+			{

+				o = list.pop();

+				if( o instanceof Number )

+					ret[i] = ((Number)o).byteValue();

+			}

+			return ret;

+		}

+		if( c == char.class )

+		{

+			if( len == 0 ) return EMPTY_CHAR_ARRAY;

+			Object o;

+			char[] ret = new char[len];

+			for(int i=len-1;i>=0;i--)

+			{

+				o = list.pop();

+				if( o instanceof Character )

+					ret[i] = ((Character)o).charValue();

+			}

+			return ret;

+		}

+		if( c == short.class )

+		{

+			if( len == 0 ) return EMPTY_SHORT_ARRAY;

+			Object o;

+			short[] ret = new short[len];

+			for(int i=len-1;i>=0;i--)

+			{

+				o = list.pop();

+				if( o instanceof Number )

+					ret[i] = ((Number)o).shortValue();

+			}

+			return ret;

+		}

+

+		Object ret = Array.newInstance(c, len);

+		for(int i=len-1;i>=0;i--)

+			Array.set(ret, i, list.pop());

+		return ret;

+	}

+

+	private static String name(Class<?>[] types)

+	{

+		StringBuilder sb = new StringBuilder();

+		for(int i=0;i<types.length;i++)

+		{

+			if( i > 0 )

+				sb.append(", ");

+			sb.append(types[i].getName());

+		}

+		return sb.toString();

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSON.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSON.java
new file mode 100644
index 0000000..828d9b8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSON.java
@@ -0,0 +1,761 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+import java.io.IOException;

+

+import java.io.Reader;

+import java.io.StringReader;

+import java.io.StringWriter;

+import java.io.Writer;

+

+import com.alibaba.dubbo.common.bytecode.Wrapper;

+import com.alibaba.dubbo.common.utils.Stack;

+

+/**

+ * JSON.

+ * 

+ * @author qian.lei

+ */

+

+public class JSON

+{

+	public static final char LBRACE = '{', RBRACE = '}';

+

+	public static final char LSQUARE = '[', RSQUARE = ']';

+

+	public static final char COMMA = ',', COLON = ':', QUOTE = '"';

+

+	public static final String NULL = "null";

+

+	static final JSONConverter DEFAULT_CONVERTER = new GenericJSONConverter();

+

+	// state.

+	public static final byte END = 0, START = 1, OBJECT_ITEM = 2, OBJECT_VALUE = 3, ARRAY_ITEM = 4;

+

+	private static class Entry

+	{

+		byte state;

+		Object value;

+		Entry(byte s, Object v){ state = s; value = v; }

+	}

+

+	private JSON(){}

+

+	/**

+	 * json string.

+	 * 

+	 * @param obj object.

+	 * @return json string.

+	 * @throws IOException.

+	 */

+	public static String json(Object obj) throws IOException

+	{

+		if( obj == null ) return NULL;

+		StringWriter sw = new StringWriter();

+		try

+		{

+			json(obj, sw);

+			return sw.getBuffer().toString();

+		}

+		finally{ sw.close(); }

+	}

+

+	/**

+	 * write json.

+	 * 

+	 * @param obj object.

+	 * @param writer writer.

+	 * @throws IOException.

+	 */

+	public static void json(Object obj, Writer writer) throws IOException

+    {

+	    json(obj, writer, false);

+    }

+	

+	public static void json(Object obj, Writer writer, boolean writeClass) throws IOException

+	{

+		if( obj == null )

+			writer.write(NULL);

+		else

+			json(obj, new JSONWriter(writer), writeClass);

+	}

+

+	/**

+	 * json string.

+	 * 

+	 * @param obj object.

+	 * @param properties property name array.

+	 * @return json string.

+	 * @throws IOException.

+	 */

+	public static String json(Object obj, String[] properties) throws IOException

+	{

+		if( obj == null ) return NULL;

+		StringWriter sw = new StringWriter();

+		try

+		{

+			json(obj, properties, sw);

+			return sw.getBuffer().toString();

+		}

+		finally{ sw.close(); }

+	}

+	

+	public static void json(Object obj, final String[] properties, Writer writer) throws IOException

+    {

+	    json(obj, properties, writer, false);

+    }

+

+	/**

+	 * write json.

+	 * 

+	 * @param obj object.

+	 * @param properties property name array.

+	 * @param writer writer.

+	 * @throws IOException.

+	 */

+	public static void json(Object obj, final String[] properties, Writer writer, boolean writeClass) throws IOException

+	{

+		if( obj == null )

+			writer.write(NULL);

+		else

+			json(obj, properties, new JSONWriter(writer), writeClass);

+	}

+

+	private static void json(Object obj, JSONWriter jb, boolean writeClass) throws IOException

+	{

+		if( obj == null )

+			jb.valueNull();

+		else

+			DEFAULT_CONVERTER.writeValue(obj, jb, writeClass);

+	}

+

+	private static void json(Object obj, String[] properties, JSONWriter jb, boolean writeClass) throws IOException

+	{

+		if( obj == null )

+		{

+			jb.valueNull();

+		}

+		else

+		{

+			Wrapper wrapper = Wrapper.getWrapper(obj.getClass());

+

+			Object value;

+			jb.objectBegin();

+			for( String prop : properties )

+			{

+				jb.objectItem(prop);

+				value = wrapper.getPropertyValue(obj, prop);

+				if( value == null )

+					jb.valueNull();

+				else

+					DEFAULT_CONVERTER.writeValue(value, jb, writeClass);

+			}

+			jb.objectEnd();

+		}

+	}

+

+	/**

+	 * parse json.

+	 * 

+	 * @param json json source.

+	 * @return JSONObject or JSONArray or Boolean or Long or Double or String or null

+	 * @throws ParseException

+	 */

+	public static Object parse(String json) throws ParseException

+	{

+		StringReader reader = new StringReader(json);

+		try{ return parse(reader); }

+		catch(IOException e){ throw new ParseException(e.getMessage()); }

+		finally{ reader.close(); }

+	}

+

+	/**

+	 * parse json.

+	 * 

+	 * @param reader reader.

+	 * @return JSONObject or JSONArray or Boolean or Long or Double or String or null

+	 * @throws IOException

+	 * @throws ParseException

+	 */

+	public static Object parse(Reader reader) throws IOException, ParseException

+	{

+		return parse(reader, JSONToken.ANY);

+	}

+

+	/**

+	 * parse json.

+	 * 

+	 * @param json json string.

+	 * @param type target type.

+	 * @return result.

+	 * @throws ParseException

+	 */

+	public static <T> T parse(String json, Class<T> type) throws ParseException

+	{

+		StringReader reader = new StringReader(json);

+		try{ return parse(reader, type); }

+		catch(IOException e){ throw new ParseException(e.getMessage()); }

+		finally{ reader.close(); }

+	}

+

+	/**

+	 * parse json

+	 * 

+	 * @param reader json source.

+	 * @param type target type.

+	 * @return result.

+	 * @throws IOException

+	 * @throws ParseException

+	 */

+	@SuppressWarnings("unchecked")

+	public static <T> T parse(Reader reader, Class<T> type) throws IOException, ParseException

+	{

+		return (T)parse(reader, new J2oVisitor(type, DEFAULT_CONVERTER), JSONToken.ANY);

+	}

+

+	/**

+	 * parse json.

+	 * 

+	 * @param json json string.

+	 * @param types target type array.

+	 * @return result.

+	 * @throws ParseException

+	 */

+	public static Object[] parse(String json, Class<?>[] types) throws ParseException

+	{

+		StringReader reader = new StringReader(json);

+		try{ return (Object[])parse(reader, types); }

+		catch(IOException e){ throw new ParseException(e.getMessage()); }

+		finally{ reader.close(); }

+	}

+

+	/**

+	 * parse json.

+	 * 

+	 * @param reader json source.

+	 * @param types target type array.

+	 * @return result.

+	 * @throws IOException

+	 * @throws ParseException

+	 */

+	public static Object[] parse(Reader reader, Class<?>[] types) throws IOException, ParseException

+	{

+		return (Object[])parse(reader, new J2oVisitor(types, DEFAULT_CONVERTER), JSONToken.LSQUARE);

+	}

+

+	/**

+	 * parse json.

+	 * 

+	 * @param json json string.

+	 * @param handler handler.

+	 * @return result.

+	 * @throws ParseException

+	 */

+	public static Object parse(String json, JSONVisitor handler) throws ParseException

+	{

+		StringReader reader = new StringReader(json);

+		try{ return parse(reader, handler); }

+		catch(IOException e){ throw new ParseException(e.getMessage()); }

+		finally{ reader.close(); }

+	}

+

+	/**

+	 * parse json.

+	 * 

+	 * @param reader json source.

+	 * @param handler handler.

+	 * @return resule.

+	 * @throws IOException

+	 * @throws ParseException

+	 */

+	public static Object parse(Reader reader, JSONVisitor handler) throws IOException, ParseException

+	{

+		return parse(reader, handler, JSONToken.ANY);

+	}

+

+	private static Object parse(Reader reader, int expect) throws IOException, ParseException

+	{

+		JSONReader jr = new JSONReader(reader);

+		JSONToken token = jr.nextToken(expect);

+

+		byte state = START;

+		Object value = null, tmp;

+		Stack<Entry> stack = new Stack<Entry>();

+

+		do

+		{

+			switch( state )

+			{

+				case END:

+					throw new ParseException("JSON source format error.");

+				case START:

+				{

+					switch( token.type )

+					{

+						case JSONToken.NULL: case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:

+						{

+							state = END;

+							value = token.value;

+							break;

+						}

+						case JSONToken.LSQUARE:

+						{

+							state = ARRAY_ITEM;

+							value = new JSONArray();

+							break;

+						}

+						case JSONToken.LBRACE:

+						{

+							state = OBJECT_ITEM;

+							value = new JSONObject();

+							break;

+						}

+						default:

+							throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");

+					}

+					break;

+				}

+				case ARRAY_ITEM:

+				{

+					switch( token.type )

+					{

+						case JSONToken.COMMA:

+							break;

+						case JSONToken.NULL: case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:

+						{

+							((JSONArray)value).add(token.value);

+							break;

+						}

+						case JSONToken.RSQUARE: // end of array.

+						{

+							if( stack.isEmpty() )

+							{

+								state = END;

+							}

+							else

+							{

+								Entry entry = stack.pop();

+								state = entry.state;

+								value = entry.value;

+							}

+							break;

+						}

+						case JSONToken.LSQUARE: // array begin.

+						{

+							tmp = new JSONArray();

+							((JSONArray)value).add(tmp);

+							stack.push(new Entry(state, value));

+

+							state = ARRAY_ITEM;

+							value = tmp;

+							break;

+						}

+						case JSONToken.LBRACE: // object begin.

+						{

+							tmp = new JSONObject();

+							((JSONArray)value).add(tmp);

+							stack.push(new Entry(state, value));

+

+							state = OBJECT_ITEM;

+							value = tmp;

+							break;

+						}

+						default:

+							throw new ParseException("Unexcepted token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");

+					}

+					break;

+				}

+				case OBJECT_ITEM:

+				{

+					switch( token.type )

+					{

+						case JSONToken.COMMA:

+							break;

+						case JSONToken.IDENT: // item name.

+						{

+							stack.push(new Entry(OBJECT_ITEM, (String)token.value));

+							state = OBJECT_VALUE;

+							break;

+						}

+						case JSONToken.NULL:

+						{

+							stack.push(new Entry(OBJECT_ITEM, "null"));

+							state = OBJECT_VALUE;

+							break;

+						}

+						case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:

+						{

+							stack.push(new Entry(OBJECT_ITEM, token.value.toString()));

+							state = OBJECT_VALUE;

+							break;

+						}

+						case JSONToken.RBRACE: // end of object.

+						{

+							if( stack.isEmpty() )

+							{

+								state = END;

+							}

+							else

+							{

+								Entry entry = stack.pop();

+								state = entry.state;

+								value = entry.value;

+							}

+							break;

+						}

+						default:

+							throw new ParseException("Unexcepted token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'");

+					}

+					break;

+				}

+				case OBJECT_VALUE:

+				{

+					switch( token.type )

+					{

+						case JSONToken.COLON:

+							break;

+						case JSONToken.NULL: case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:

+						{

+							((JSONObject)value).put((String)stack.pop().value, token.value);

+							state = OBJECT_ITEM;

+							break;

+						}

+						case JSONToken.LSQUARE: // array begin.

+						{

+							tmp = new JSONArray();

+							((JSONObject)value).put((String)stack.pop().value, tmp);

+							stack.push(new Entry(OBJECT_ITEM, value));

+

+							state = ARRAY_ITEM;

+							value = tmp;

+							break;

+						}

+						case JSONToken.LBRACE: // object begin.

+						{

+							tmp = new JSONObject();

+							((JSONObject)value).put((String)stack.pop().value, tmp);

+							stack.push(new Entry(OBJECT_ITEM, value));

+

+							state = OBJECT_ITEM;

+							value = tmp;

+							break;

+						}

+						default:

+							throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");

+					}

+					break;

+				}

+				default:

+					throw new ParseException("Unexcepted state.");

+			}

+		}

+		while( ( token = jr.nextToken() ) != null );

+		stack.clear();

+		return value;

+	}

+

+	private static Object parse(Reader reader, JSONVisitor handler, int expect) throws IOException, ParseException

+	{

+		JSONReader jr = new JSONReader(reader);

+		JSONToken token = jr.nextToken(expect);

+

+		Object value = null;

+		int state = START, index = 0;

+		Stack<int[]> states = new Stack<int[]>();

+		boolean pv = false;

+

+		handler.begin();

+		do

+		{

+			switch( state )

+			{

+				case END:

+					throw new ParseException("JSON source format error.");

+				case START:

+				{

+					switch( token.type )

+					{

+						case JSONToken.NULL:

+						{

+							value = token.value;

+							state = END;

+							pv = true;

+							break;

+						}

+						case JSONToken.BOOL:

+						{

+							value = token.value;

+							state = END;

+							pv = true;

+							break;

+						}

+						case JSONToken.INT:

+						{

+							value = token.value;

+							state = END;

+							pv = true;

+							break;

+						}

+						case JSONToken.FLOAT:

+						{

+							value = token.value;

+							state = END;

+							pv = true;

+							break;

+						}

+						case JSONToken.STRING:

+						{

+							value = token.value;

+							state = END;

+							pv = true;

+							break;

+						}

+						case JSONToken.LSQUARE:

+						{

+							handler.arrayBegin();

+							state = ARRAY_ITEM;

+							break;

+						}

+						case JSONToken.LBRACE:

+						{

+							handler.objectBegin();

+							state = OBJECT_ITEM;

+							break;

+						}

+						default:

+							throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");

+					}

+					break;

+				}

+				case ARRAY_ITEM:

+				{

+					switch( token.type )

+					{

+						case JSONToken.COMMA:

+							break;

+						case JSONToken.NULL:

+						{

+							handler.arrayItem(index++);

+							handler.arrayItemValue(index, token.value, true);

+							break;

+						}

+						case JSONToken.BOOL:

+						{

+							handler.arrayItem(index++);

+							handler.arrayItemValue(index, token.value, true);

+							break;

+						}

+						case JSONToken.INT:

+						{

+							handler.arrayItem(index++);

+							handler.arrayItemValue(index, token.value, true);

+							break;

+						}

+						case JSONToken.FLOAT:

+						{

+							handler.arrayItem(index++);

+							handler.arrayItemValue(index, token.value, true);

+							break;

+						}

+						case JSONToken.STRING:

+						{

+							handler.arrayItem(index++);

+							handler.arrayItemValue(index, token.value, true);

+							break;

+						}

+						case JSONToken.LSQUARE:

+						{

+							handler.arrayItem(index++);

+							states.push(new int[]{state, index});

+

+							index = 0;

+							state = ARRAY_ITEM;

+							handler.arrayBegin();

+							break;

+						}

+						case JSONToken.LBRACE:

+						{

+							handler.arrayItem(index++);

+							states.push(new int[]{state, index});

+

+							index = 0;

+							state = OBJECT_ITEM;

+							handler.objectBegin();

+							break;

+						}

+						case JSONToken.RSQUARE:

+						{

+							if( states.isEmpty() )

+							{

+								value = handler.arrayEnd(index);

+								state = END;

+							}

+							else

+							{

+								value = handler.arrayEnd(index);

+								int[] tmp = states.pop();

+								state = tmp[0];

+								index = tmp[1];

+

+								switch( state )

+								{

+									case ARRAY_ITEM:

+									{

+										handler.arrayItemValue(index, value, false);

+										break;

+									}

+									case OBJECT_ITEM:

+									{

+										handler.objectItemValue(value, false);

+										break;

+									}

+								}

+							}

+							break;

+						}

+						default:

+							throw new ParseException("Unexcepted token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");

+					}

+					break;

+				}

+				case OBJECT_ITEM:

+				{

+					switch( token.type )

+					{

+						case JSONToken.COMMA:

+							break;

+						case JSONToken.IDENT:

+						{

+							handler.objectItem((String)token.value);

+							state = OBJECT_VALUE;

+							break;

+						}

+						case JSONToken.NULL:

+						{

+							handler.objectItem("null");

+							state = OBJECT_VALUE;

+							break;

+						}

+						case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:

+						{

+							handler.objectItem(token.value.toString());

+							state = OBJECT_VALUE;

+							break;

+						}

+						case JSONToken.RBRACE:

+						{

+							if( states.isEmpty() )

+							{

+								value = handler.objectEnd(index);

+								state = END;

+							}

+							else

+							{

+								value = handler.objectEnd(index);

+								int[] tmp = states.pop();

+								state = tmp[0];

+								index = tmp[1];

+

+								switch( state )

+								{

+									case ARRAY_ITEM:

+									{

+										handler.arrayItemValue(index, value, false);

+										break;

+									}

+									case OBJECT_ITEM:

+									{

+										handler.objectItemValue(value, false);

+										break;

+									}

+								}

+							}

+							break;

+						}

+						default:

+							throw new ParseException("Unexcepted token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'");

+					}

+					break;

+				}

+				case OBJECT_VALUE:

+				{

+					switch( token.type )

+					{

+						case JSONToken.COLON:

+							break;

+						case JSONToken.NULL:

+						{

+							handler.objectItemValue(token.value, true);

+							state = OBJECT_ITEM;

+							break;

+						}

+						case JSONToken.BOOL:

+						{

+							handler.objectItemValue(token.value, true);

+							state = OBJECT_ITEM;

+							break;

+						}

+						case JSONToken.INT:

+						{

+							handler.objectItemValue(token.value, true);

+							state = OBJECT_ITEM;

+							break;

+						}

+						case JSONToken.FLOAT:

+						{

+							handler.objectItemValue(token.value, true);

+							state = OBJECT_ITEM;

+							break;

+						}

+						case JSONToken.STRING:

+						{

+							handler.objectItemValue(token.value, true);

+							state = OBJECT_ITEM;

+							break;

+						}

+						case JSONToken.LSQUARE:

+						{

+							states.push(new int[]{OBJECT_ITEM, index});

+

+							index = 0;

+							state = ARRAY_ITEM;

+							handler.arrayBegin();

+							break;

+						}

+						case JSONToken.LBRACE:

+						{

+							states.push(new int[]{OBJECT_ITEM, index});

+

+							index = 0;

+							state = OBJECT_ITEM;

+							handler.objectBegin();

+							break;

+						}

+						default:

+							throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");

+					}

+					break;

+				}

+				default:

+					throw new ParseException("Unexcepted state.");

+			}

+		}

+		while( ( token = jr.nextToken() ) != null );

+		states.clear();

+		return handler.end(value, pv);

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONArray.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONArray.java
new file mode 100644
index 0000000..e25ef9b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONArray.java
@@ -0,0 +1,198 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+import java.io.IOException;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+

+/**

+ * JSONArray.

+ * 

+ * @author qian.lei

+ */

+

+public class JSONArray implements JSONNode

+{

+	private List<Object> mArray = new ArrayList<Object>();

+

+	/**

+	 * get.

+	 * 

+	 * @param index index.

+	 * @return boolean or long or double or String or JSONArray or JSONObject or null.

+	 */

+	public Object get(int index)

+	{

+		return mArray.get(index);

+	}

+

+	/**

+	 * get boolean value.

+	 * 

+	 * @param index index.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public boolean getBoolean(int index, boolean def)

+	{

+		Object tmp = mArray.get(index);

+		return tmp != null && tmp instanceof Boolean ? ((Boolean)tmp).booleanValue() : def;

+	}

+

+	/**

+	 * get int value.

+	 * 

+	 * @param index index.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public int getInt(int index, int def)

+	{

+		Object tmp = mArray.get(index);

+		return tmp != null && tmp instanceof Number ? ((Number)tmp).intValue() : def;

+	}

+

+	/**

+	 * get long value.

+	 * 

+	 * @param index index.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public long getLong(int index, long def)

+	{

+		Object tmp = mArray.get(index);

+		return tmp != null && tmp instanceof Number ? ((Number)tmp).longValue() : def;

+	}

+

+	/**

+	 * get float value.

+	 * 

+	 * @param index index.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public float getFloat(int index, float def)

+	{

+		Object tmp = mArray.get(index);

+		return tmp != null && tmp instanceof Number ? ((Number)tmp).floatValue() : def;

+	}

+

+	/**

+	 * get double value.

+	 * 

+	 * @param index index.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public double getDouble(int index, double def)

+	{

+		Object tmp = mArray.get(index);

+		return tmp != null && tmp instanceof Number ? ((Number)tmp).doubleValue() : def;

+	}

+

+	/**

+	 * get string value.

+	 * 

+	 * @param index index.

+	 * @return value or default value.

+	 */

+	public String getString(int index)

+	{

+		Object tmp = mArray.get(index);

+		return tmp == null ? null : tmp.toString();

+	}

+

+	/**

+	 * get JSONArray value.

+	 * 

+	 * @param index index.

+	 * @return value or default value.

+	 */

+	public JSONArray getArray(int index)

+	{

+		Object tmp = mArray.get(index);

+		return tmp == null ? null : tmp instanceof JSONArray ? (JSONArray)tmp : null;

+	}

+

+	/**

+	 * get JSONObject value.

+	 * 

+	 * @param index index.

+	 * @return value or default value.

+	 */

+	public JSONObject getObject(int index)

+	{

+		Object tmp = mArray.get(index);

+		return tmp == null ? null : tmp instanceof JSONObject ? (JSONObject)tmp : null;

+	}

+

+	/**

+	 * get array length.

+	 * 

+	 * @return length.

+	 */

+	public int length()

+	{

+		return mArray.size();

+	}

+

+	/**

+	 * add item.

+	 */

+	public void add(Object ele)

+	{

+		mArray.add(ele);

+	}

+

+	/**

+	 * add items.

+	 */

+	public void addAll(Object[] eles)

+	{

+		for( Object ele : eles )

+			mArray.add(ele);

+	}

+

+	/**

+	 * add items.

+	 */

+	public void addAll(Collection<?> c)

+	{

+		mArray.addAll(c);

+	}

+

+	/**

+	 * write json.

+	 * 

+	 * @param jc json converter

+	 * @param jb json builder.

+	 */

+	public void writeJSON(JSONConverter jc, JSONWriter jb, boolean writeClass) throws IOException

+	{

+		jb.arrayBegin();

+		for( Object item : mArray )

+		{

+			if( item == null )

+				jb.valueNull();

+			else

+				jc.writeValue(item, jb, writeClass);

+		}

+		jb.arrayEnd();

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONConverter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONConverter.java
new file mode 100644
index 0000000..921460a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONConverter.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;
+
+import java.io.IOException;
+
+/**
+ * JSON converter.
+ * 
+ * @author qianlei
+ */
+
+public interface JSONConverter
+{
+	/**
+	 * write object.
+	 * 
+	 * @param obj obj.
+	 * @param builder builder.
+	 * @throws IOException
+	 */
+	void writeValue(Object obj, JSONWriter builder, boolean writeClass) throws IOException;
+
+	/**
+	 * convert json value to target class.
+	 * 
+	 * @param type target type.
+	 * @param jv json value.
+	 * @return target object.
+	 * @throws IOException.
+	 */
+	Object readValue(Class<?> type, Object jv) throws IOException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONNode.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONNode.java
new file mode 100644
index 0000000..131f8b0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONNode.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+import java.io.IOException;

+

+/**

+ * JSONSerializable.

+ * 

+ * @author qian.lei

+ */

+

+interface JSONNode

+{

+	/**

+	 * write json string.

+	 * 

+	 * @param jc json converter.

+	 * @param jb json builder.

+	 * @throws IOException

+	 */

+	void writeJSON(JSONConverter jc, JSONWriter jb, boolean writeClass) throws IOException;

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONObject.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONObject.java
new file mode 100644
index 0000000..5ca2172
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONObject.java
@@ -0,0 +1,223 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+import java.io.IOException;

+import java.util.HashMap;

+import java.util.Iterator;

+import java.util.Map;

+

+/**

+ * JSONObject.

+ * 

+ * @author qian.lei

+ */

+

+public class JSONObject implements JSONNode

+{

+	private Map<String,Object> mMap = new HashMap<String,Object>();

+

+	/**

+	 * get.

+	 * 

+	 * @param key key.

+	 * @return boolean or long or double or String or JSONArray or JSONObject or null.

+	 */

+	public Object get(String key)

+	{

+		return mMap.get(key);

+	}

+

+	/**

+	 * get boolean value.

+	 * 

+	 * @param key key.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public boolean getBoolean(String key, boolean def)

+	{

+		Object tmp = mMap.get(key);

+		return tmp != null && tmp instanceof Boolean ? (Boolean)tmp : def;

+	}

+

+	/**

+	 * get int value.

+	 * 

+	 * @param key key.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public int getInt(String key, int def)

+	{

+		Object tmp = mMap.get(key);

+		return tmp != null && tmp instanceof Number ? ((Number)tmp).intValue() : def;

+	}

+

+	/**

+	 * get long value.

+	 * 

+	 * @param key key.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public long getLong(String key, long def)

+	{

+		Object tmp = mMap.get(key);

+		return tmp != null && tmp instanceof Number ? ((Number)tmp).longValue() : def;

+	}

+

+	/**

+	 * get float value.

+	 * 

+	 * @param key key.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public float getFloat(String key, float def)

+	{

+		Object tmp = mMap.get(key);

+		return tmp != null && tmp instanceof Number ? ((Number)tmp).floatValue() : def;

+	}

+

+	/**

+	 * get double value.

+	 * 

+	 * @param key key.

+	 * @param def default value.

+	 * @return value or default value.

+	 */

+	public double getDouble(String key, double def)

+	{

+		Object tmp = mMap.get(key);

+		return tmp != null && tmp instanceof Number ? ((Number)tmp).doubleValue() : def;

+	}

+

+	/**

+	 * get string value.

+	 * 

+	 * @param key key.

+	 * @return value or default value.

+	 */

+	public String getString(String key)

+	{

+		Object tmp = mMap.get(key);

+		return tmp == null ? null : tmp.toString();

+	}

+

+	/**

+	 * get JSONArray value.

+	 * 

+	 * @param key key.

+	 * @return value or default value.

+	 */

+	public JSONArray getArray(String key)

+	{

+		Object tmp = mMap.get(key);

+		return tmp == null ? null : tmp instanceof JSONArray ? (JSONArray)tmp : null;

+	}

+

+	/**

+	 * get JSONObject value.

+	 * 

+	 * @param key key.

+	 * @return value or default value.

+	 */

+	public JSONObject getObject(String key)

+	{

+		Object tmp = mMap.get(key);

+		return tmp == null ? null : tmp instanceof JSONObject ? (JSONObject)tmp : null;

+	}

+

+	/**

+	 * get key iterator.

+	 * 

+	 * @return key iterator.

+	 */

+	public Iterator<String> keys()

+	{

+		return mMap.keySet().iterator();

+	}

+

+	/**

+	 * contains key.

+	 * 

+	 * @param key key.

+	 * @return contains or not.

+	 */

+	public boolean contains(String key)

+	{

+		return mMap.containsKey(key);

+	}

+

+	/**

+	 * put value.

+	 * 

+	 * @param name name.

+	 * @param value value.

+	 */

+	public void put(String name, Object value)

+	{

+		mMap.put(name, value);

+	}

+

+	/**

+	 * put all.

+	 * 

+	 * @param names name array.

+	 * @param values value array.

+	 */

+	public void putAll(String[] names, Object[] values)

+	{

+		for(int i=0,len=Math.min(names.length, values.length);i<len;i++)

+			mMap.put(names[i], values[i]);

+	}

+

+	/**

+	 * put all.

+	 * 

+	 * @param map map.

+	 */

+	public void putAll(Map<String, Object> map)

+	{

+		for( Map.Entry<String, Object> entry : map.entrySet() )

+			mMap.put(entry.getKey(), entry.getValue());

+	}

+

+	/**

+	 * write json.

+	 * 

+	 * @param jc json converter.

+	 * @param jb json builder.

+	 */

+	public void writeJSON(JSONConverter jc, JSONWriter jb, boolean writeClass) throws IOException

+	{

+		String key;

+		Object value;

+		jb.objectBegin();

+		for( Map.Entry<String,Object> entry : mMap.entrySet() )

+		{

+			key = entry.getKey();

+			jb.objectItem(key);

+			value = entry.getValue();

+			if( value == null )

+				jb.valueNull();

+			else

+				jc.writeValue(value, jb, writeClass);

+		}

+		jb.objectEnd();

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONReader.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONReader.java
new file mode 100644
index 0000000..f32a721
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONReader.java
@@ -0,0 +1,75 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * JSON reader.
+ * 
+ * @author qian.lei
+ */
+
+public class JSONReader
+{
+	private static ThreadLocal<Yylex> LOCAL_LEXER = new ThreadLocal<Yylex>(){};
+
+	private Yylex mLex;
+
+	public JSONReader(InputStream is, String charset) throws UnsupportedEncodingException
+	{
+		this(new InputStreamReader(is, charset));
+	}
+
+	public JSONReader(Reader reader)
+	{
+		mLex = getLexer(reader);
+	}
+
+	public JSONToken nextToken() throws IOException, ParseException
+	{
+		return mLex.yylex();
+	}
+
+	public JSONToken nextToken(int expect) throws IOException, ParseException
+	{
+		JSONToken ret = mLex.yylex();
+		if( ret == null )
+			throw new ParseException("EOF error.");
+		if( expect != JSONToken.ANY && expect != ret.type )
+			throw new ParseException("Unexcepted token.");
+		return ret;
+	}
+
+	private static Yylex getLexer(Reader reader)
+	{
+		Yylex ret = LOCAL_LEXER.get();
+		if( ret == null )
+		{
+			ret = new Yylex(reader);
+			LOCAL_LEXER.set(ret);
+		}
+		else
+		{
+			ret.yyreset(reader);
+		}
+		return ret;
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONToken.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONToken.java
new file mode 100644
index 0000000..b3c925d
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONToken.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+/**

+ * JSONToken.

+ * 

+ * @author qian.lei

+ */

+

+public class JSONToken

+{

+	// token type

+	public static final int ANY = 0, IDENT = 0x01, LBRACE = 0x02, LSQUARE = 0x03, RBRACE = 0x04, RSQUARE = 0x05, COMMA = 0x06, COLON = 0x07;

+

+	public static final int NULL = 0x10, BOOL = 0x11, INT = 0x12, FLOAT = 0x13, STRING = 0x14, ARRAY = 0x15, OBJECT = 0x16;

+

+	public final int type;

+

+	public final Object value;

+

+	JSONToken(int t)

+	{

+		this(t, null);

+	}

+

+	JSONToken(int t, Object v)

+	{

+		type = t;

+		value = v;

+	}

+

+	static String token2string(int t)

+	{

+		switch( t )

+		{

+			case LBRACE: return "{";

+			case RBRACE: return "}";

+			case LSQUARE: return "[";

+			case RSQUARE: return "]";

+			case COMMA: return ",";

+			case COLON: return ":";

+			case IDENT: return "IDENT";

+			case NULL: return "NULL";

+			case BOOL: return "BOOL VALUE";

+			case INT: return "INT VALUE";

+			case FLOAT: return "FLOAT VALUE";

+			case STRING: return "STRING VALUE";

+			default: return "ANY";

+		}

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONVisitor.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONVisitor.java
new file mode 100644
index 0000000..d04f531
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONVisitor.java
@@ -0,0 +1,109 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+/**

+ * JSONVisitor.

+ * 

+ * @author qian.lei

+ */

+

+public interface JSONVisitor

+{

+	public static final String CLASS_PROPERTY = "class";

+

+	/**

+	 * parse begin .

+	 */

+	void begin();

+

+	/**

+	 * parse end.

+	 * 

+	 * @param obj root obj.

+	 * @param isValue is json value.

+	 * @return parse result.

+	 * @throws ParseException

+	 */

+	Object end(Object obj, boolean isValue) throws ParseException;

+

+	/**

+	 * object begin.

+	 * 

+	 * @throws ParseException

+	 */

+	void objectBegin() throws ParseException;

+

+	/**

+	 * object end, return object value.

+	 * 

+	 * @param count property count.

+	 * @return object value.

+	 * @throws ParseException

+	 */

+	Object objectEnd(int count) throws ParseException;

+

+	/**

+	 * object property name.

+	 * 

+	 * @param name name.

+	 * @throws ParseException

+	 */

+	void objectItem(String name) throws ParseException;

+

+	/**

+	 * object property value.

+	 * 

+	 * @param obj obj.

+	 * @param isValue is json value.

+	 * @throws ParseException

+	 */

+	void objectItemValue(Object obj, boolean isValue) throws ParseException;

+

+	/**

+	 * array begin.

+	 * 

+	 * @throws ParseException

+	 */

+	void arrayBegin() throws ParseException;

+

+	/**

+	 * array end, return array value.

+	 * 

+	 * @param count count.

+	 * @return array value.

+	 * @throws ParseException

+	 */

+	Object arrayEnd(int count) throws ParseException;

+

+	/**

+	 * array item.

+	 * 

+	 * @param index index.

+	 * @throws ParseException

+	 */

+	void arrayItem(int index) throws ParseException;

+

+	/**

+	 * array item.

+	 * 

+	 * @param index index.

+	 * @param obj item.

+	 * @param isValue is json value.

+	 * @throws ParseException

+	 */

+	void arrayItemValue(int index, Object obj, boolean isValue) throws ParseException;

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONWriter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONWriter.java
new file mode 100644
index 0000000..9d90bf0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONWriter.java
@@ -0,0 +1,325 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+import java.io.IOException;

+import java.io.OutputStream;

+import java.io.OutputStreamWriter;

+import java.io.UnsupportedEncodingException;

+import java.io.Writer;

+

+import com.alibaba.dubbo.common.utils.Stack;

+

+/**

+ * JSON Writer.

+ * 

+ * w.objectBegin().objectItem("name").valueString("qianlei").objectEnd() = {name:"qianlei"}.

+ * 

+ * @author qian.lei

+ */

+

+public class JSONWriter

+{

+	private static final byte UNKNOWN = 0, ARRAY = 1, OBJECT = 2, OBJECT_VALUE = 3;

+

+	private static class State

+	{

+		private byte type;

+		private int itemCount = 0;

+

+		State(byte t){ type = t; }

+	}

+

+	private Writer mWriter;

+

+	private State mState = new State(UNKNOWN);

+

+	private Stack<State> mStack = new Stack<State>();

+

+	public JSONWriter(Writer writer)

+	{

+		mWriter = writer;

+	}

+

+	public JSONWriter(OutputStream is, String charset) throws UnsupportedEncodingException

+	{

+		mWriter = new OutputStreamWriter(is, charset);

+	}

+

+	/**

+	 * object begin.

+	 * 

+	 * @return this.

+	 * @throws IOException.

+	 */

+	public JSONWriter objectBegin() throws IOException

+	{

+		beforeValue();

+

+		mWriter.write(JSON.LBRACE);

+		mStack.push(mState);

+		mState = new State(OBJECT);

+		return this;

+	}

+

+	/**

+	 * object end.

+	 * 

+	 * @return this.

+	 * @throws IOException.

+	 */

+	public JSONWriter objectEnd() throws IOException

+	{

+		mWriter.write(JSON.RBRACE);

+		mState = mStack.pop();

+		return this;

+	}

+

+	/**

+	 * object item.

+	 * 

+	 * @param name name.

+	 * @return this.

+	 * @throws IOException.

+	 */

+	public JSONWriter objectItem(String name) throws IOException

+	{

+		beforeObjectItem();

+

+		mWriter.write(JSON.QUOTE);

+		mWriter.write(escape(name));

+		mWriter.write(JSON.QUOTE);

+		mWriter.write(JSON.COLON);

+		return this;

+	}

+

+	/**

+	 * array begin.

+	 * 

+	 * @return this.

+	 * @throws IOException.

+	 */

+	public JSONWriter arrayBegin() throws IOException

+	{

+		beforeValue();

+

+		mWriter.write(JSON.LSQUARE);

+		mStack.push(mState);

+		mState = new State(ARRAY);

+		return this;

+	}

+

+	/**

+	 * array end, return array value.

+	 * 

+	 * @return this.

+	 * @throws IOException.

+	 */

+	public JSONWriter arrayEnd() throws IOException

+	{

+		mWriter.write(JSON.RSQUARE);

+		mState = mStack.pop();

+		return this;

+	}

+

+	/**

+	 * value.

+	 * 

+	 * @return this.

+	 * @throws IOException.

+	 */

+	public JSONWriter valueNull() throws IOException

+	{

+		beforeValue();

+

+		mWriter.write(JSON.NULL);

+		return this;

+	}

+

+	/**

+	 * value.

+	 * 

+	 * @param value value.

+	 * @return this.

+	 * @throws IOException

+	 */

+	public JSONWriter valueString(String value) throws IOException

+	{

+		beforeValue();

+

+		mWriter.write(JSON.QUOTE);

+		mWriter.write(escape(value));

+		mWriter.write(JSON.QUOTE);

+		return this;

+	}

+

+	/**

+	 * value.

+	 * 

+	 * @param value value.

+	 * @return this.

+	 * @throws IOException

+	 */

+	public JSONWriter valueBoolean(boolean value) throws IOException

+	{

+		beforeValue();

+

+		mWriter.write(value ? "true" : "false");

+		return this;

+	}

+

+	/**

+	 * value.

+	 * 

+	 * @param value value.

+	 * @return this.

+	 * @throws IOException

+	 */

+	public JSONWriter valueInt(int value) throws IOException

+	{

+		beforeValue();

+

+		mWriter.write(String.valueOf(value));

+		return this;

+	}

+

+	/**

+	 * value.

+	 * 

+	 * @param value value.

+	 * @return this.

+	 * @throws IOException

+	 */

+	public JSONWriter valueLong(long value) throws IOException

+	{

+		beforeValue();

+

+		mWriter.write(String.valueOf(value));

+		return this;

+	}

+

+	/**

+	 * value.

+	 * 

+	 * @param value value.

+	 * @return this.

+	 * @throws IOException

+	 */

+	public JSONWriter valueFloat(float value) throws IOException

+	{

+		beforeValue();

+

+		mWriter.write(String.valueOf(value));

+		return this;

+	}

+

+	/**

+	 * value.

+	 * 

+	 * @param value value.

+	 * @return this.

+	 * @throws IOException

+	 */

+	public JSONWriter valueDouble(double value) throws IOException

+	{

+		beforeValue();

+

+		mWriter.write(String.valueOf(value));

+		return this;

+	}

+

+	private void beforeValue() throws IOException

+	{

+		switch( mState.type )

+		{

+			case ARRAY:

+				if( mState.itemCount++ > 0 )

+					mWriter.write(JSON.COMMA);

+				return;

+			case OBJECT:

+				throw new IOException("Must call objectItem first.");

+			case OBJECT_VALUE:

+				mState.type = OBJECT;

+				return;

+		}

+	}

+

+	private void beforeObjectItem() throws IOException

+	{

+		switch( mState.type )

+		{

+			case OBJECT_VALUE:

+				mWriter.write(JSON.NULL);

+			case OBJECT:

+				mState.type = OBJECT_VALUE;

+				if( mState.itemCount++ > 0 )

+					mWriter.write(JSON.COMMA);

+				return;

+			default:

+				throw new IOException("Must call objectBegin first.");

+		}

+	}

+

+	private static final String[] CONTROL_CHAR_MAP = new String[]{

+		"\\u0000","\\u0001","\\u0002","\\u0003","\\u0004","\\u0005","\\u0006","\\u0007",

+		"\\b","\\t","\\n","\\u000b","\\f","\\r","\\u000e","\\u000f",

+		"\\u0010","\\u0011","\\u0012","\\u0013","\\u0014","\\u0015","\\u0016","\\u0017",

+		"\\u0018","\\u0019","\\u001a","\\u001b","\\u001c","\\u001d","\\u001e","\\u001f"

+	};

+

+	private static String escape(String str)

+	{

+		if( str == null )

+			return str;

+		int len = str.length();

+		if( len == 0 )

+			return str;

+

+        char c;

+        StringBuilder sb = null;

+        for(int i=0;i<len;i++)

+        {

+        	c = str.charAt(i);

+        	if( c < ' ' ) // control char.

+        	{

+        		if( sb == null )

+        		{

+        			sb = new StringBuilder(len<<1);

+					sb.append(str,0,i);

+        		}

+            	sb.append(CONTROL_CHAR_MAP[c]);

+        	}

+        	else

+        	{

+            	switch( c )

+            	{

+            		case '\\': case '/': case '"':

+    	        		if( sb == null )

+    	        		{

+    	        			sb = new StringBuilder(len<<1);

+    						sb.append(str,0,i);

+    	        		}

+            			sb.append('\\').append(c);

+            			break;

+            		default:

+            			if( sb != null )

+            				sb.append(c);

+            	}

+        	}

+        }

+        return sb == null ? str : sb.toString();

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/ParseException.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/ParseException.java
new file mode 100644
index 0000000..5bda123
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/ParseException.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+/**

+ * ParseException.

+ * 

+ * @author qian.lei

+ */

+

+public class ParseException extends Exception

+{

+	private static final long serialVersionUID = 8611884051738966316L;

+

+	public ParseException()

+	{

+		super();

+	}

+

+	public ParseException(String message)

+	{

+	    super(message);

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/Yylex.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/Yylex.java
new file mode 100644
index 0000000..0e3163b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/Yylex.java
@@ -0,0 +1,800 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;
+
+/**
+ * This class is a scanner generated by 
+ * <a href="http://www.jflex.de/">JFlex</a> 1.4.3
+ * on 7/3/10 3:12 AM from the specification file
+ * <tt>/Users/qianlei/dev/proj/dubbo-1.1/dubbo.common/src/main/java/com/alibaba/dubbo/common/json/json.flex</tt>
+ */
+public class Yylex {
+
+  /** This character denotes the end of file */
+  public static final int YYEOF = -1;
+
+  /** initial size of the lookahead buffer */
+  private static final int ZZ_BUFFERSIZE = 16384;
+
+  /** lexical states */
+  public static final int STR2 = 4;
+  public static final int STR1 = 2;
+  public static final int YYINITIAL = 0;
+
+  /**
+   * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
+   * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
+   *                  at the beginning of a line
+   * l is of the form l = 2*k, k a non negative integer
+   */
+  private static final int ZZ_LEXSTATE[] = { 
+     0,  0,  1,  1,  2, 2
+  };
+
+  /** 
+   * Translates characters to character classes
+   */
+  private static final String ZZ_CMAP_PACKED = 
+    "\11\0\1\13\1\13\2\0\1\13\22\0\1\13\1\0\1\10\1\0"+
+    "\1\2\2\0\1\11\3\0\1\7\1\43\1\4\1\5\1\14\12\1"+
+    "\1\44\6\0\1\33\3\3\1\6\1\32\5\2\1\34\1\2\1\36"+
+    "\3\2\1\25\1\35\1\24\1\26\5\2\1\41\1\12\1\42\1\0"+
+    "\1\2\1\0\1\27\1\15\2\3\1\23\1\16\5\2\1\30\1\2"+
+    "\1\17\3\2\1\20\1\31\1\21\1\22\5\2\1\37\1\0\1\40"+
+    "\uff82\0";
+
+  /** 
+   * Translates characters to character classes
+   */
+  private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED);
+
+  /** 
+   * Translates DFA states to action switch labels.
+   */
+  private static final int [] ZZ_ACTION = zzUnpackAction();
+
+  private static final String ZZ_ACTION_PACKED_0 =
+    "\3\0\1\1\1\2\1\3\1\1\1\4\1\5\1\6"+
+    "\6\3\1\7\1\10\1\11\1\12\1\13\1\14\1\15"+
+    "\1\16\1\0\1\15\3\0\6\3\1\17\1\20\1\21"+
+    "\1\22\1\23\1\24\1\25\1\26\1\0\1\27\2\30"+
+    "\1\0\6\3\1\0\1\3\1\31\1\32\1\3\1\0"+
+    "\1\33\1\0\1\34";
+
+  private static int [] zzUnpackAction() {
+    int [] result = new int[63];
+    int offset = 0;
+    offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackAction(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+
+  /** 
+   * Translates a state to a row index in the transition table
+   */
+  private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
+
+  private static final String ZZ_ROWMAP_PACKED_0 =
+    "\0\0\0\45\0\112\0\157\0\224\0\271\0\336\0\157"+
+    "\0\157\0\u0103\0\u0128\0\u014d\0\u0172\0\u0197\0\u01bc\0\u01e1"+
+    "\0\157\0\157\0\157\0\157\0\157\0\157\0\u0206\0\157"+
+    "\0\u022b\0\u0250\0\u0275\0\u029a\0\u02bf\0\u02e4\0\u0309\0\u032e"+
+    "\0\u0353\0\u0378\0\u039d\0\157\0\157\0\157\0\157\0\157"+
+    "\0\157\0\157\0\157\0\u03c2\0\157\0\u03e7\0\u040c\0\u040c"+
+    "\0\u0431\0\u0456\0\u047b\0\u04a0\0\u04c5\0\u04ea\0\u050f\0\u0534"+
+    "\0\271\0\271\0\u0559\0\u057e\0\271\0\u05a3\0\157";
+
+  private static int [] zzUnpackRowMap() {
+    int [] result = new int[63];
+    int offset = 0;
+    offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackRowMap(String packed, int offset, int [] result) {
+    int i = 0;  /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int high = packed.charAt(i++) << 16;
+      result[j++] = high | packed.charAt(i++);
+    }
+    return j;
+  }
+
+  /** 
+   * The transition table of the DFA
+   */
+  private static final int ZZ_TRANS [] = {
+    3, 4, 5, 5, 6, 3, 5, 3, 7, 8, 
+    3, 9, 3, 5, 10, 11, 5, 12, 5, 5, 
+    13, 5, 5, 5, 5, 5, 14, 5, 5, 5, 
+    15, 16, 17, 18, 19, 20, 21, 22, 22, 22, 
+    22, 22, 22, 22, 22, 23, 22, 24, 22, 22, 
+    22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 
+    22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 
+    22, 22, 22, 22, 25, 25, 25, 25, 25, 25, 
+    25, 25, 25, 23, 26, 25, 25, 25, 25, 25, 
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 
+    25, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 
+    -1, -1, -1, 27, 28, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, 28, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, 5, 5, 5, -1, 
+    -1, 5, -1, -1, -1, -1, -1, -1, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, -1, -1, -1, -1, 
+    -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    9, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, 5, 5, 5, 
+    -1, -1, 5, -1, -1, -1, -1, -1, -1, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 29, 
+    5, 5, 5, 5, 5, 5, 5, -1, -1, -1, 
+    -1, -1, -1, -1, 5, 5, 5, -1, -1, 5, 
+    -1, -1, -1, -1, -1, -1, 5, 5, 5, 5, 
+    5, 30, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, -1, -1, -1, -1, -1, -1, 
+    -1, 5, 5, 5, -1, -1, 5, -1, -1, -1, 
+    -1, -1, -1, 5, 5, 5, 31, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, -1, -1, -1, -1, -1, -1, -1, 5, 5, 
+    5, -1, -1, 5, -1, -1, -1, -1, -1, -1, 
+    5, 5, 5, 5, 5, 5, 5, 5, 32, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, -1, -1, 
+    -1, -1, -1, -1, -1, 5, 5, 5, -1, -1, 
+    5, -1, -1, -1, -1, -1, -1, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 33, 5, 5, 5, -1, -1, -1, -1, -1, 
+    -1, -1, 5, 5, 5, -1, -1, 5, -1, -1, 
+    -1, -1, -1, -1, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 34, 5, 5, 5, 5, 5, 5, 
+    5, 5, -1, -1, -1, -1, -1, -1, 22, 22, 
+    22, 22, 22, 22, 22, 22, -1, 22, -1, 22, 
+    22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 
+    22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 
+    22, 22, 22, 22, 22, -1, -1, -1, -1, -1, 
+    -1, -1, -1, 35, -1, 36, -1, 37, 38, 39, 
+    40, 41, 42, 43, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, 25, 25, 25, 25, 25, 25, 25, 25, 
+    25, -1, -1, 25, 25, 25, 25, 25, 25, 25, 
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 
+    25, 25, 25, 25, 25, 25, 25, 25, 25, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, 44, 36, 
+    -1, 37, 38, 39, 40, 41, 42, 43, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, 45, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, 46, -1, -1, 47, -1, -1, 
+    47, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, 5, 5, 5, -1, -1, 5, -1, -1, -1, 
+    -1, -1, -1, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 48, 5, 5, 5, 5, 5, 
+    5, -1, -1, -1, -1, -1, -1, -1, 5, 5, 
+    5, -1, -1, 5, -1, -1, -1, -1, -1, -1, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 49, 5, 5, 5, 5, 5, 5, -1, -1, 
+    -1, -1, -1, -1, -1, 5, 5, 5, -1, -1, 
+    5, -1, -1, -1, -1, -1, -1, 5, 5, 5, 
+    5, 5, 50, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, -1, -1, -1, -1, -1, 
+    -1, -1, 5, 5, 5, -1, -1, 5, -1, -1, 
+    -1, -1, -1, -1, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 51, 5, 5, 5, 5, 5, 5, 
+    5, 5, -1, -1, -1, -1, -1, -1, -1, 5, 
+    5, 5, -1, -1, 5, -1, -1, -1, -1, -1, 
+    -1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 52, 5, 5, -1, 
+    -1, -1, -1, -1, -1, -1, 5, 5, 5, -1, 
+    -1, 5, -1, -1, -1, -1, -1, -1, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 53, 5, 5, -1, -1, -1, -1, 
+    -1, -1, -1, 54, -1, 54, -1, -1, 54, -1, 
+    -1, -1, -1, -1, -1, 54, 54, -1, -1, -1, 
+    -1, 54, -1, -1, -1, 54, -1, -1, 54, 54, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    45, -1, -1, -1, -1, 28, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, 28, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, 46, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, 5, 5, 5, -1, -1, 5, 
+    -1, -1, -1, -1, -1, -1, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 55, 5, 
+    5, 5, 5, 5, -1, -1, -1, -1, -1, -1, 
+    -1, 5, 5, 5, -1, -1, 5, -1, -1, -1, 
+    -1, -1, -1, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 56, 5, 5, 5, 5, 5, 
+    5, -1, -1, -1, -1, -1, -1, -1, 5, 5, 
+    5, -1, -1, 5, -1, -1, -1, -1, -1, -1, 
+    5, 5, 5, 5, 5, 5, 57, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, -1, -1, 
+    -1, -1, -1, -1, -1, 5, 5, 5, -1, -1, 
+    57, -1, -1, -1, -1, -1, -1, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, -1, -1, -1, -1, -1, 
+    -1, -1, 5, 5, 5, -1, -1, 5, -1, -1, 
+    -1, -1, -1, -1, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    58, 5, -1, -1, -1, -1, -1, -1, -1, 5, 
+    5, 5, -1, -1, 5, -1, -1, -1, -1, -1, 
+    -1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 56, 5, 5, -1, 
+    -1, -1, -1, -1, -1, -1, 59, -1, 59, -1, 
+    -1, 59, -1, -1, -1, -1, -1, -1, 59, 59, 
+    -1, -1, -1, -1, 59, -1, -1, -1, 59, -1, 
+    -1, 59, 59, -1, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, 5, 5, 5, -1, -1, 5, -1, 
+    -1, -1, -1, -1, -1, 5, 5, 5, 5, 5, 
+    5, 60, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, -1, -1, -1, -1, -1, -1, -1, 
+    5, 5, 5, -1, -1, 60, -1, -1, -1, -1, 
+    -1, -1, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    -1, -1, -1, -1, -1, -1, -1, 61, -1, 61, 
+    -1, -1, 61, -1, -1, -1, -1, -1, -1, 61, 
+    61, -1, -1, -1, -1, 61, -1, -1, -1, 61, 
+    -1, -1, 61, 61, -1, -1, -1, -1, -1, -1, 
+    -1, -1, -1, -1, 62, -1, 62, -1, -1, 62, 
+    -1, -1, -1, -1, -1, -1, 62, 62, -1, -1, 
+    -1, -1, 62, -1, -1, -1, 62, -1, -1, 62, 
+    62, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+  };
+
+  /* error codes */
+  private static final int ZZ_UNKNOWN_ERROR = 0;
+  private static final int ZZ_NO_MATCH = 1;
+  private static final int ZZ_PUSHBACK_2BIG = 2;
+
+  /* error messages for the codes above */
+  private static final String ZZ_ERROR_MSG[] = {
+    "Unkown internal scanner error",
+    "Error: could not match input",
+    "Error: pushback value was too large"
+  };
+
+  /**
+   * ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code>
+   */
+  private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
+
+  private static final String ZZ_ATTRIBUTE_PACKED_0 =
+    "\3\0\1\11\3\1\2\11\7\1\6\11\1\1\1\11"+
+    "\1\0\1\1\3\0\6\1\10\11\1\0\1\11\2\1"+
+    "\1\0\6\1\1\0\4\1\1\0\1\1\1\0\1\11";
+
+  private static int [] zzUnpackAttribute() {
+    int [] result = new int[63];
+    int offset = 0;
+    offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackAttribute(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+  /** the input device */
+  private java.io.Reader zzReader;
+
+  /** the current state of the DFA */
+  private int zzState;
+
+  /** the current lexical state */
+  private int zzLexicalState = YYINITIAL;
+
+  /** this buffer contains the current text to be matched and is
+      the source of the yytext() string */
+  private char zzBuffer[] = new char[ZZ_BUFFERSIZE];
+
+  /** the textposition at the last accepting state */
+  private int zzMarkedPos;
+
+  /** the current text position in the buffer */
+  private int zzCurrentPos;
+
+  /** startRead marks the beginning of the yytext() string in the buffer */
+  private int zzStartRead;
+
+  /** endRead marks the last character in the buffer, that has been read
+      from input */
+  private int zzEndRead;
+
+  /** number of newlines encountered up to the start of the matched text */
+  //private int yyline;
+
+  /** the number of characters up to the start of the matched text */
+  //private int yychar;
+
+  /**
+   * the number of characters from the last newline up to the start of the 
+   * matched text
+   */
+  //private int yycolumn;
+
+  /** 
+   * zzAtBOL == true <=> the scanner is currently at the beginning of a line
+   */
+  //private boolean zzAtBOL = true;
+
+  /** zzAtEOF == true <=> the scanner is at the EOF */
+  private boolean zzAtEOF;
+
+  /** denotes if the user-EOF-code has already been executed */
+  //private boolean zzEOFDone;
+
+  /* user code: */
+private StringBuffer sb;
+
+
+  /**
+   * Creates a new scanner
+   * There is also a java.io.InputStream version of this constructor.
+   *
+   * @param   in  the java.io.Reader to read input from.
+   */
+  Yylex(java.io.Reader in) {
+    this.zzReader = in;
+  }
+
+  /**
+   * Creates a new scanner.
+   * There is also java.io.Reader version of this constructor.
+   *
+   * @param   in  the java.io.Inputstream to read input from.
+   */
+  Yylex(java.io.InputStream in) {
+    this(new java.io.InputStreamReader(in));
+  }
+
+  /** 
+   * Unpacks the compressed character translation table.
+   *
+   * @param packed   the packed character translation table
+   * @return         the unpacked character translation table
+   */
+  private static char [] zzUnpackCMap(String packed) {
+    char [] map = new char[0x10000];
+    int i = 0;  /* index in packed string  */
+    int j = 0;  /* index in unpacked array */
+    while (i < 122) {
+      int  count = packed.charAt(i++);
+      char value = packed.charAt(i++);
+      do map[j++] = value; while (--count > 0);
+    }
+    return map;
+  }
+
+
+  /**
+   * Refills the input buffer.
+   *
+   * @return      <code>false</code>, iff there was new input.
+   * 
+   * @exception   java.io.IOException  if any I/O-Error occurs
+   */
+  private boolean zzRefill() throws java.io.IOException {
+
+    /* first: make room (if you can) */
+    if (zzStartRead > 0) {
+      System.arraycopy(zzBuffer, zzStartRead,
+                       zzBuffer, 0,
+                       zzEndRead-zzStartRead);
+
+      /* translate stored positions */
+      zzEndRead-= zzStartRead;
+      zzCurrentPos-= zzStartRead;
+      zzMarkedPos-= zzStartRead;
+      zzStartRead = 0;
+    }
+
+    /* is the buffer big enough? */
+    if (zzCurrentPos >= zzBuffer.length) {
+      /* if not: blow it up */
+      char newBuffer[] = new char[zzCurrentPos*2];
+      System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length);
+      zzBuffer = newBuffer;
+    }
+
+    /* finally: fill the buffer with new input */
+    int numRead = zzReader.read(zzBuffer, zzEndRead,
+                                            zzBuffer.length-zzEndRead);
+
+    if (numRead > 0) {
+      zzEndRead+= numRead;
+      return false;
+    }
+    // unlikely but not impossible: read 0 characters, but not at end of stream    
+    if (numRead == 0) {
+      int c = zzReader.read();
+      if (c == -1) {
+        return true;
+      } else {
+        zzBuffer[zzEndRead++] = (char) c;
+        return false;
+      }     
+    }
+
+	// numRead < 0
+    return true;
+  }
+
+    
+  /**
+   * Closes the input stream.
+   */
+  public final void yyclose() throws java.io.IOException {
+    zzAtEOF = true;            /* indicate end of file */
+    zzEndRead = zzStartRead;  /* invalidate buffer    */
+
+    if (zzReader != null)
+      zzReader.close();
+  }
+
+
+  /**
+   * Resets the scanner to read from a new input stream.
+   * Does not close the old reader.
+   *
+   * All internal variables are reset, the old input stream 
+   * <b>cannot</b> be reused (internal buffer is discarded and lost).
+   * Lexical state is set to <tt>ZZ_INITIAL</tt>.
+   *
+   * @param reader   the new input stream 
+   */
+  public final void yyreset(java.io.Reader reader) {
+    zzReader = reader;
+    //zzAtBOL  = true;
+    zzAtEOF  = false;
+    //zzEOFDone = false;
+    zzEndRead = zzStartRead = 0;
+    zzCurrentPos = zzMarkedPos = 0;
+    //yyline = yychar = yycolumn = 0;
+    zzLexicalState = YYINITIAL;
+  }
+
+
+  /**
+   * Returns the current lexical state.
+   */
+  public final int yystate() {
+    return zzLexicalState;
+  }
+
+
+  /**
+   * Enters a new lexical state
+   *
+   * @param newState the new lexical state
+   */
+  public final void yybegin(int newState) {
+    zzLexicalState = newState;
+  }
+
+
+  /**
+   * Returns the text matched by the current regular expression.
+   */
+  public final String yytext() {
+    return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead );
+  }
+
+
+  /**
+   * Returns the character at position <tt>pos</tt> from the 
+   * matched text. 
+   * 
+   * It is equivalent to yytext().charAt(pos), but faster
+   *
+   * @param pos the position of the character to fetch. 
+   *            A value from 0 to yylength()-1.
+   *
+   * @return the character at position pos
+   */
+  public final char yycharat(int pos) {
+    return zzBuffer[zzStartRead+pos];
+  }
+
+
+  /**
+   * Returns the length of the matched text region.
+   */
+  public final int yylength() {
+    return zzMarkedPos-zzStartRead;
+  }
+
+
+  /**
+   * Reports an error that occured while scanning.
+   *
+   * In a wellformed scanner (no or only correct usage of 
+   * yypushback(int) and a match-all fallback rule) this method 
+   * will only be called with things that "Can't Possibly Happen".
+   * If this method is called, something is seriously wrong
+   * (e.g. a JFlex bug producing a faulty scanner etc.).
+   *
+   * Usual syntax/scanner level error handling should be done
+   * in error fallback rules.
+   *
+   * @param   errorCode  the code of the errormessage to display
+   */
+  private void zzScanError(int errorCode) {
+    String message;
+    try {
+      message = ZZ_ERROR_MSG[errorCode];
+    }
+    catch (ArrayIndexOutOfBoundsException e) {
+      message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
+    }
+
+    throw new Error(message);
+  } 
+
+
+  /**
+   * Pushes the specified amount of characters back into the input stream.
+   *
+   * They will be read again by then next call of the scanning method
+   *
+   * @param number  the number of characters to be read again.
+   *                This number must not be greater than yylength()!
+   */
+  public void yypushback(int number)  {
+    if ( number > yylength() )
+      zzScanError(ZZ_PUSHBACK_2BIG);
+
+    zzMarkedPos -= number;
+  }
+
+
+  /**
+   * Resumes scanning until the next regular expression is matched,
+   * the end of input is encountered or an I/O-Error occurs.
+   *
+   * @return      the next token
+   * @exception   java.io.IOException  if any I/O-Error occurs
+   */
+  public JSONToken yylex() throws java.io.IOException, ParseException {
+    int zzInput;
+    int zzAction;
+
+    // cached fields:
+    int zzCurrentPosL;
+    int zzMarkedPosL;
+    int zzEndReadL = zzEndRead;
+    char [] zzBufferL = zzBuffer;
+    char [] zzCMapL = ZZ_CMAP;
+
+    int [] zzTransL = ZZ_TRANS;
+    int [] zzRowMapL = ZZ_ROWMAP;
+    int [] zzAttrL = ZZ_ATTRIBUTE;
+
+    while (true) {
+      zzMarkedPosL = zzMarkedPos;
+
+      zzAction = -1;
+
+      zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
+  
+      zzState = ZZ_LEXSTATE[zzLexicalState];
+
+
+      zzForAction: {
+        while (true) {
+    
+          if (zzCurrentPosL < zzEndReadL)
+            zzInput = zzBufferL[zzCurrentPosL++];
+          else if (zzAtEOF) {
+            zzInput = YYEOF;
+            break zzForAction;
+          }
+          else {
+            // store back cached positions
+            zzCurrentPos  = zzCurrentPosL;
+            zzMarkedPos   = zzMarkedPosL;
+            boolean eof = zzRefill();
+            // get translated positions and possibly new buffer
+            zzCurrentPosL  = zzCurrentPos;
+            zzMarkedPosL   = zzMarkedPos;
+            zzBufferL      = zzBuffer;
+            zzEndReadL     = zzEndRead;
+            if (eof) {
+              zzInput = YYEOF;
+              break zzForAction;
+            }
+            else {
+              zzInput = zzBufferL[zzCurrentPosL++];
+            }
+          }
+          int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ];
+          if (zzNext == -1) break zzForAction;
+          zzState = zzNext;
+
+          int zzAttributes = zzAttrL[zzState];
+          if ( (zzAttributes & 1) == 1 ) {
+            zzAction = zzState;
+            zzMarkedPosL = zzCurrentPosL;
+            if ( (zzAttributes & 8) == 8 ) break zzForAction;
+          }
+
+        }
+      }
+
+      // store back cached position
+      zzMarkedPos = zzMarkedPosL;
+
+      switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
+        case 25: 
+          { return new JSONToken(JSONToken.NULL, null);
+          }
+        case 29: break;
+        case 13: 
+          { sb.append(yytext());
+          }
+        case 30: break;
+        case 18: 
+          { sb.append('\b');
+          }
+        case 31: break;
+        case 9: 
+          { return new JSONToken(JSONToken.LSQUARE);
+          }
+        case 32: break;
+        case 2: 
+          { Long val = Long.valueOf(yytext()); return new JSONToken(JSONToken.INT, val);
+          }
+        case 33: break;
+        case 16: 
+          { sb.append('\\');
+          }
+        case 34: break;
+        case 8: 
+          { return new JSONToken(JSONToken.RBRACE);
+          }
+        case 35: break;
+        case 26: 
+          { return new JSONToken(JSONToken.BOOL, Boolean.TRUE);
+          }
+        case 36: break;
+        case 23: 
+          { sb.append('\'');
+          }
+        case 37: break;
+        case 5: 
+          { sb = new StringBuffer(); yybegin(STR2);
+          }
+        case 38: break;
+        case 27: 
+          { return new JSONToken(JSONToken.BOOL, Boolean.FALSE);
+          }
+        case 39: break;
+        case 12: 
+          { return new JSONToken(JSONToken.COLON);
+          }
+        case 40: break;
+        case 21: 
+          { sb.append('\r');
+          }
+        case 41: break;
+        case 3: 
+          { return new JSONToken(JSONToken.IDENT, yytext());
+          }
+        case 42: break;
+        case 28: 
+          { try{ sb.append((char)Integer.parseInt(yytext().substring(2),16)); }catch(Exception e){ throw new ParseException(e.getMessage()); }
+          }
+        case 43: break;
+        case 10: 
+          { return new JSONToken(JSONToken.RSQUARE);
+          }
+        case 44: break;
+        case 17: 
+          { sb.append('/');
+          }
+        case 45: break;
+        case 11: 
+          { return new JSONToken(JSONToken.COMMA);
+          }
+        case 46: break;
+        case 15: 
+          { sb.append('"');
+          }
+        case 47: break;
+        case 24: 
+          { Double val = Double.valueOf(yytext()); return new JSONToken(JSONToken.FLOAT, val);
+          }
+        case 48: break;
+        case 1: 
+          { throw new ParseException("Unexpected char [" + yytext() +"]");
+          }
+        case 49: break;
+        case 19: 
+          { sb.append('\f');
+          }
+        case 50: break;
+        case 7: 
+          { return new JSONToken(JSONToken.LBRACE);
+          }
+        case 51: break;
+        case 14: 
+          { yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString());
+          }
+        case 52: break;
+        case 22: 
+          { sb.append('\t');
+          }
+        case 53: break;
+        case 4: 
+          { sb = new StringBuffer(); yybegin(STR1);
+          }
+        case 54: break;
+        case 20: 
+          { sb.append('\n');
+          }
+        case 55: break;
+        case 6: 
+          { 
+          }
+        case 56: break;
+        default: 
+          if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
+            zzAtEOF = true;
+            return null;
+          } 
+          else {
+            zzScanError(ZZ_NO_MATCH);
+          }
+      }
+    }
+  }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Level.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Level.java
new file mode 100644
index 0000000..060c9ec
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Level.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.logger;

+

+/**

+ * Level

+ * 

+ * @author william.liangf

+ */

+public enum Level {

+

+    /**

+     * ALL

+     */

+	ALL,

+	

+	/**

+     * TRACE

+     */

+	TRACE,

+	

+	/**

+     * DEBUG

+     */

+	DEBUG,

+	

+	/**

+     * INFO

+     */

+	INFO,

+	

+	/**

+     * WARN

+     */

+	WARN,

+	

+	/**

+     * ERROR

+     */

+	ERROR,

+

+	/**

+     * OFF

+     */

+	OFF

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Logger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Logger.java
new file mode 100644
index 0000000..d7d71cd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Logger.java
@@ -0,0 +1,170 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.logger;
+
+/**
+ * 日志接口 <p/> 声明:引用自commons-logging
+ *
+ * @author william.liangf
+ */
+public interface Logger {
+
+    /**
+     * 输出跟踪信息
+     *
+     * @param msg 信息内容
+     */
+    public void trace(String msg);
+
+    /**
+     * 输出跟踪信息
+     *
+     * @param e 异常信息
+     */
+    public void trace(Throwable e);
+    
+    /**
+     * 输出跟踪信息
+     *
+     * @param msg 信息内容
+     * @param e 异常信息
+     */
+    public void trace(String msg, Throwable e);
+
+	/**
+	 * 输出调试信息
+	 *
+	 * @param msg 信息内容
+	 */
+	public void debug(String msg);
+
+	/**
+     * 输出调试信息
+     *
+     * @param e 异常信息
+     */
+	public void debug(Throwable e);
+	
+	/**
+	 * 输出调试信息
+	 *
+	 * @param msg 信息内容
+	 * @param e 异常信息
+	 */
+	public void debug(String msg, Throwable e);
+
+	/**
+	 * 输出普通信息
+	 *
+	 * @param msg 信息内容
+	 */
+	public void info(String msg);
+
+	/**
+     * 输出普通信息
+     *
+     * @param e 异常信息
+     */
+	public void info(Throwable e);
+	
+	/**
+	 * 输出普通信息
+	 *
+	 * @param msg 信息内容
+	 * @param e 异常信息
+	 */
+	public void info(String msg, Throwable e);
+
+	/**
+	 * 输出警告信息
+	 *
+	 * @param msg 信息内容
+	 */
+	public void warn(String msg);
+	
+	/**
+     * 输出警告信息
+     *
+     * @param e 异常信息
+     */
+	public void warn(Throwable e);
+
+	/**
+	 * 输出警告信息
+	 *
+	 * @param msg 信息内容
+	 * @param e 异常信息
+	 */
+	public void warn(String msg, Throwable e);
+
+	/**
+	 * 输出错误信息
+	 *
+	 * @param msg 信息内容
+	 */
+	public void error(String msg);
+	
+	/**
+     * 输出错误信息
+     *
+     * @param e 异常信息
+     */
+	public void error(Throwable e);
+
+	/**
+	 * 输出错误信息
+	 *
+	 * @param msg 信息内容
+	 * @param e 异常信息
+	 */
+	public void error(String msg, Throwable e);
+
+    /**
+     * 跟踪信息是否开启
+     *
+     * @return 是否开启
+     */
+    public boolean isTraceEnabled();
+
+	/**
+	 * 调试信息是否开启
+	 *
+	 * @return 是否开启
+	 */
+	public boolean isDebugEnabled();
+
+	/**
+	 * 普通信息是否开启
+	 *
+	 * @return 是否开启
+	 */
+	public boolean isInfoEnabled();
+
+	/**
+	 * 警告信息是否开启
+	 *
+	 * @return 是否开启
+	 */
+	public boolean isWarnEnabled();
+	
+	/**
+	 * 错误信息是否开启
+	 *
+	 * @return 是否开启
+	 */
+	public boolean isErrorEnabled();
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactory.java
new file mode 100644
index 0000000..f0dfadf
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactory.java
@@ -0,0 +1,108 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.logger;
+
+import java.io.File;
+
+import com.alibaba.dubbo.common.logger.support.FailsafeLogger;
+import com.alibaba.dubbo.common.logger.support.JdkLoggerFactory;
+import com.alibaba.dubbo.common.logger.support.Log4jLoggerFactory;
+
+/**
+ * 日志输出器工厂
+ * 
+ * @author william.liangf
+ */
+public class LoggerFactory {
+
+	private LoggerFactory() {
+	}
+
+	private static volatile LoggerFactorySupport LOGGER_FACTORY;
+
+	// 查找常用的日志框架
+	static {
+		try {
+            setLoggerFactory(new Log4jLoggerFactory());
+        } catch (Throwable e1) {
+        	setLoggerFactory(new JdkLoggerFactory());
+        }
+	}
+
+	/**
+	 * 设置日志输出器供给器
+	 * 
+	 * @param loggerFactory
+	 *            日志输出器供给器
+	 */
+	public static void setLoggerFactory(LoggerFactorySupport loggerFactory) {
+		if (loggerFactory != null) {
+			Logger logger = loggerFactory.getLogger(LoggerFactory.class.getName());
+			logger.info("using logger: " + loggerFactory.getClass().getName());
+			LoggerFactory.LOGGER_FACTORY = loggerFactory;
+		}
+	}
+
+	/**
+	 * 获取日志输出器
+	 * 
+	 * @param key
+	 *            分类键
+	 * @return 日志输出器, 后验条件: 不返回null.
+	 */
+	public static Logger getLogger(Class<?> key) {
+		return new FailsafeLogger(LOGGER_FACTORY.getLogger(key));
+	}
+
+	/**
+	 * 获取日志输出器
+	 * 
+	 * @param key
+	 *            分类键
+	 * @return 日志输出器, 后验条件: 不返回null.
+	 */
+	public static Logger getLogger(String key) {
+		return new FailsafeLogger(LOGGER_FACTORY.getLogger(key));
+	}
+	
+	/**
+	 * 动态设置输出日志级别
+	 * 
+	 * @param level 日志级别
+	 */
+	public static void setLevel(Level level) {
+		LOGGER_FACTORY.setLevel(level);
+	}
+
+	/**
+	 * 获取日志级别
+	 * 
+	 * @return 日志级别
+	 */
+	public static Level getLevel() {
+		return LOGGER_FACTORY.getLevel();
+	}
+	
+	/**
+	 * 获取日志文件
+	 * 
+	 * @return 日志文件
+	 */
+	public static File getFile() {
+		return LOGGER_FACTORY.getFile();
+	}
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactorySupport.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactorySupport.java
new file mode 100644
index 0000000..19f1817
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactorySupport.java
@@ -0,0 +1,71 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.logger;
+
+import java.io.File;
+
+/**
+ * 日志输出器供给器
+ *
+ * @author william.liangf
+ */
+public interface LoggerFactorySupport {
+	
+	/**
+	 * 获取日志输出器
+	 *
+	 * @param key 分类键
+	 * @return 日志输出器, 后验条件: 不返回null.
+	 */
+	Logger getLogger(Class<?> key);
+
+	/**
+	 * 获取日志输出器
+	 *
+	 * @param key 分类键
+	 * @return 日志输出器, 后验条件: 不返回null.
+	 */
+	Logger getLogger(String key);
+	
+	/**
+	 * 设置输出等级
+	 * 
+	 * @param level 输出等级
+	 */
+	void setLevel(Level level);
+	
+	/**
+	 * 获取当前日志等级

+	 * 
+	 * @return 当前日志等级
+	 */
+	Level getLevel();
+	
+	/**
+	 * 获取当前日志文件

+	 * 
+	 * @return 当前日志文件
+	 */
+	File getFile();
+	
+	/**
+	 * 设置输出日志文件

+	 * 
+	 * @param file 输出日志文件
+	 */
+	void setFile(File file);
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/FailsafeLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/FailsafeLogger.java
new file mode 100644
index 0000000..8be45be
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/FailsafeLogger.java
@@ -0,0 +1,179 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.logger.support;
+
+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.utils.NetUtils;

+
+public class FailsafeLogger implements Logger {
+
+	private final Logger logger;
+
+	public FailsafeLogger(Logger logger) {
+		this.logger = logger;
+	}

+	

+	private String appendContextMessage(String msg) {

+	    return " [DUBBO] " + msg + ", dubbo version: " + Version.getVersion() + ", current host: " + NetUtils.getLogHost();

+	}
+
+    public void trace(String msg, Throwable e) {
+        try {
+            logger.trace(appendContextMessage(msg), e);
+        } catch (Throwable t) {
+        }
+    }
+
+    public void trace(Throwable e) {
+        try {
+            logger.trace(e);
+        } catch (Throwable t) {
+        }
+    }
+
+    public void trace(String msg) {
+        try {
+            logger.trace(appendContextMessage(msg));
+        } catch (Throwable t) {
+        }
+    }
+
+	public void debug(String msg, Throwable e) {
+		try {
+			logger.debug(appendContextMessage(msg), e);
+		} catch (Throwable t) {
+		}
+	}
+
+    public void debug(Throwable e) {
+        try {
+            logger.debug(e);
+        } catch (Throwable t) {
+        }
+    }
+
+	public void debug(String msg) {
+		try {
+			logger.debug(appendContextMessage(msg));
+		} catch (Throwable t) {
+		}
+	}
+
+	public void info(String msg, Throwable e) {
+		try {
+			logger.info(appendContextMessage(msg), e);
+		} catch (Throwable t) {
+		}
+	}
+
+	public void info(String msg) {
+		try {
+			logger.info(appendContextMessage(msg));
+		} catch (Throwable t) {
+		}
+	}
+
+	public void warn(String msg, Throwable e) {
+		try {
+			logger.warn(appendContextMessage(msg), e);
+		} catch (Throwable t) {
+		}
+	}
+
+	public void warn(String msg) {
+		try {
+			logger.warn(appendContextMessage(msg));
+		} catch (Throwable t) {
+		}
+	}
+
+	public void error(String msg, Throwable e) {
+		try {
+			logger.error(appendContextMessage(msg), e);
+		} catch (Throwable t) {
+		}
+	}
+
+	public void error(String msg) {
+		try {
+			logger.error(appendContextMessage(msg));
+		} catch (Throwable t) {
+		}
+	}
+
+    public void error(Throwable e) {
+        try {
+            logger.error(e);
+        } catch (Throwable t) {
+        }
+    }
+
+    public void info(Throwable e) {
+        try {
+            logger.info(e);
+        } catch (Throwable t) {
+        }
+    }
+
+    public void warn(Throwable e) {
+        try {
+            logger.warn(e);
+        } catch (Throwable t) {
+        }
+    }
+
+    public boolean isTraceEnabled() {
+        try {
+            return logger.isTraceEnabled();
+        } catch (Throwable t) {
+            return false;
+        }
+    }
+
+	public boolean isDebugEnabled() {
+		try {
+			return logger.isDebugEnabled();
+		} catch (Throwable t) {
+			return false;
+		}
+	}
+
+	public boolean isInfoEnabled() {
+		try {
+			return logger.isInfoEnabled();
+		} catch (Throwable t) {
+			return false;
+		}
+	}
+
+	public boolean isWarnEnabled() {
+		try {
+			return logger.isWarnEnabled();
+		} catch (Throwable t) {
+			return false;
+		}
+	}
+	
+	public boolean isErrorEnabled() {
+	    try {
+	        return logger.isErrorEnabled();
+	    } catch (Throwable t) {
+	        return false;
+	    }
+	}
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLogger.java
new file mode 100644
index 0000000..ddcfebc
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLogger.java
@@ -0,0 +1,110 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.logger.support;
+
+import java.util.logging.Level;
+
+import com.alibaba.dubbo.common.logger.Logger;
+
+public class JdkLogger implements Logger {
+
+	private final java.util.logging.Logger logger;
+
+	public JdkLogger(java.util.logging.Logger logger) {
+		this.logger = logger;
+	}
+
+    public void trace(String msg) {
+        logger.log(Level.FINER, msg);
+    }
+
+    public void trace(Throwable e) {
+        logger.log(Level.FINER, e.getMessage(), e);
+    }
+
+    public void trace(String msg, Throwable e) {
+        logger.log(Level.FINER, msg, e);
+    }
+
+	public void debug(String msg) {
+		logger.log(Level.FINE, msg);
+	}
+
+    public void debug(Throwable e) {
+        logger.log(Level.FINE, e.getMessage(), e);
+    }
+
+	public void debug(String msg, Throwable e) {
+		logger.log(Level.FINE, msg, e);
+	}
+
+	public void info(String msg) {
+		logger.log(Level.INFO, msg);
+	}
+
+	public void info(String msg, Throwable e) {
+		logger.log(Level.INFO, msg, e);
+	}
+
+	public void warn(String msg) {
+		logger.log(Level.WARNING, msg);
+	}
+
+	public void warn(String msg, Throwable e) {
+		logger.log(Level.WARNING, msg, e);
+	}
+
+	public void error(String msg) {
+		logger.log(Level.SEVERE, msg);
+	}
+
+	public void error(String msg, Throwable e) {
+		logger.log(Level.SEVERE, msg, e);
+	}
+
+    public void error(Throwable e) {
+        logger.log(Level.SEVERE, e.getMessage(), e);
+    }
+
+    public void info(Throwable e) {
+        logger.log(Level.INFO, e.getMessage(), e);
+    }
+
+    public void warn(Throwable e) {
+        logger.log(Level.WARNING, e.getMessage(), e);
+    }
+
+    public boolean isTraceEnabled() {
+        return logger.isLoggable(Level.FINER);
+    }
+
+	public boolean isDebugEnabled() {
+		return logger.isLoggable(Level.FINE);
+	}
+
+	public boolean isInfoEnabled() {
+		return logger.isLoggable(Level.INFO);
+	}
+
+	public boolean isWarnEnabled() {
+		return logger.isLoggable(Level.WARNING);
+	}
+
+	public boolean isErrorEnabled() {
+		return logger.isLoggable(Level.SEVERE);
+	}
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLoggerFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLoggerFactory.java
new file mode 100644
index 0000000..fb8cb6b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLoggerFactory.java
@@ -0,0 +1,120 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.logger.support;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.util.logging.FileHandler;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+
+import com.alibaba.dubbo.common.logger.Level;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactorySupport;
+
+public class JdkLoggerFactory implements LoggerFactorySupport {
+	
+	private static final String GLOBAL_LOGGER_NAME = "global";
+
+    private File file;
+
+	public JdkLoggerFactory() {
+		try {
+			InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties");
+			if (in != null) {
+				LogManager.getLogManager().readConfiguration(in);
+			} else {
+				System.err.println("No such logging.properties in classpath for jdk logging config!");
+			}
+		} catch (Throwable t) {
+			System.err.println("Failed to load logging.properties in classpath for jdk logging config, cause: " + t.getMessage());
+		}
+		try {
+			Handler[] handlers = java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME).getHandlers();
+			for (Handler handler : handlers) {
+				if (handler instanceof FileHandler) {
+					FileHandler fileHandler = (FileHandler)handler;
+					Field field = fileHandler.getClass().getField("files");
+					File[] files =  (File[])field.get(fileHandler);
+					if (files != null && files.length > 0) {
+						file = files[0];
+					}
+				}
+			}
+		} catch (Throwable t) {
+		}
+	}
+
+	public Logger getLogger(Class<?> key) {
+		return new JdkLogger(java.util.logging.Logger.getLogger(key == null ? "" : key.getName()));
+	}
+
+	public Logger getLogger(String key) {
+		return new JdkLogger(java.util.logging.Logger.getLogger(key));
+	}
+
+	public void setLevel(Level level) {
+		java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME).setLevel(toJdkLevel(level));
+	}
+
+	public Level getLevel() {
+		return fromJdkLevel(java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME).getLevel());
+	}
+
+	public File getFile() {
+		return file;
+	}
+
+	private static java.util.logging.Level toJdkLevel(Level level) {
+		if (level == Level.ALL)
+			return java.util.logging.Level.ALL;
+		if (level == Level.TRACE)
+			return java.util.logging.Level.FINER;
+		if (level == Level.DEBUG)
+			return java.util.logging.Level.FINE;
+		if (level == Level.INFO)
+			return java.util.logging.Level.INFO;
+		if (level == Level.WARN)
+			return java.util.logging.Level.WARNING;
+		if (level == Level.ERROR)
+			return java.util.logging.Level.SEVERE;
+		// if (level == Level.OFF)
+			return java.util.logging.Level.OFF;
+	}
+
+	private static Level fromJdkLevel(java.util.logging.Level level) {
+		if (level == java.util.logging.Level.ALL)
+			return Level.ALL;
+		if (level == java.util.logging.Level.FINER)
+			return Level.TRACE;
+		if (level == java.util.logging.Level.FINE)
+			return Level.DEBUG;
+		if (level == java.util.logging.Level.INFO)
+			return Level.INFO;
+		if (level == java.util.logging.Level.WARNING)
+			return Level.WARN;
+		if (level == java.util.logging.Level.SEVERE)
+			return Level.ERROR;
+		// if (level == java.util.logging.Level.OFF)
+			return Level.OFF;
+	}
+
+    public void setFile(File file) {
+        
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLogger.java
new file mode 100644
index 0000000..c257b87
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLogger.java
@@ -0,0 +1,112 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.logger.support;
+
+import org.apache.log4j.Level;
+
+import com.alibaba.dubbo.common.logger.Logger;
+
+public class Log4jLogger implements Logger {
+
+	private static final String FQCN = FailsafeLogger.class.getName();
+
+	private final org.apache.log4j.Logger logger;
+
+	public Log4jLogger(org.apache.log4j.Logger logger) {
+		this.logger = logger;
+	}
+
+	public void trace(String msg) {
+		logger.log(FQCN, Level.TRACE, msg, null);
+	}
+
+	public void trace(Throwable e) {
+		logger.log(FQCN, Level.TRACE, e == null ? null : e.getMessage(), e);
+	}
+
+	public void trace(String msg, Throwable e) {
+		logger.log(FQCN, Level.TRACE, msg, e);
+	}
+
+	public void debug(String msg) {
+		logger.log(FQCN, Level.DEBUG, msg, null);
+	}
+
+	public void debug(Throwable e) {
+		logger.log(FQCN, Level.DEBUG, e == null ? null : e.getMessage(), e);
+	}
+
+	public void debug(String msg, Throwable e) {
+		logger.log(FQCN, Level.DEBUG, msg, e);
+	}
+
+	public void info(String msg) {
+		logger.log(FQCN, Level.INFO, msg, null);
+	}
+
+	public void info(Throwable e) {
+		logger.log(FQCN, Level.INFO, e == null ? null : e.getMessage(), e);
+	}
+
+	public void info(String msg, Throwable e) {
+		logger.log(FQCN, Level.INFO, msg, e);
+	}
+
+	public void warn(String msg) {
+		logger.log(FQCN, Level.WARN, msg, null);
+	}
+
+	public void warn(Throwable e) {
+		logger.log(FQCN, Level.WARN, e == null ? null : e.getMessage(), e);
+	}
+
+	public void warn(String msg, Throwable e) {
+		logger.log(FQCN, Level.WARN, msg, e);
+	}
+
+	public void error(String msg) {
+		logger.log(FQCN, Level.ERROR, msg, null);
+	}
+
+	public void error(Throwable e) {
+		logger.log(FQCN, Level.ERROR, e == null ? null : e.getMessage(), e);
+	}
+
+	public void error(String msg, Throwable e) {
+		logger.log(FQCN, Level.ERROR, msg, e);
+	}
+
+	public boolean isTraceEnabled() {
+		return logger.isTraceEnabled();
+	}
+
+	public boolean isDebugEnabled() {
+		return logger.isDebugEnabled();
+	}
+
+	public boolean isInfoEnabled() {
+		return logger.isInfoEnabled();
+	}
+
+	public boolean isWarnEnabled() {
+		return logger.isEnabledFor(Level.WARN);
+	}
+	
+	public boolean isErrorEnabled() {
+	    return logger.isEnabledFor(Level.ERROR);
+	}
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLoggerFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLoggerFactory.java
new file mode 100644
index 0000000..369560e
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLoggerFactory.java
@@ -0,0 +1,113 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.logger.support;
+
+import java.io.File;
+import java.util.Enumeration;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.LogManager;
+
+import com.alibaba.dubbo.common.logger.Level;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactorySupport;
+
+public class Log4jLoggerFactory implements LoggerFactorySupport {
+
+    private File            file;
+
+	@SuppressWarnings("unchecked")
+	public Log4jLoggerFactory() {
+		try {
+			org.apache.log4j.Logger logger = LogManager.getRootLogger();
+            if (logger != null) {
+                Enumeration<Appender> appenders = logger.getAllAppenders();
+                if (appenders != null) {
+                    while (appenders.hasMoreElements()) {
+                        Appender appender = appenders.nextElement();
+                        if (appender instanceof FileAppender) {
+                            FileAppender fileAppender = (FileAppender)appender;
+                            String filename = fileAppender.getFile();
+                            file = new File(filename);
+                            break;
+                        }
+                    }
+                }
+            }
+        } catch (Throwable t) {
+        }
+	}
+
+	public Logger getLogger(Class<?> key) {
+		return new Log4jLogger(LogManager.getLogger(key));
+	}
+
+	public Logger getLogger(String key) {
+		return new Log4jLogger(LogManager.getLogger(key));
+	}
+
+	public void setLevel(Level level) {
+		LogManager.getRootLogger().setLevel(toLog4jLevel(level));
+	}
+
+	public Level getLevel() {
+		return fromLog4jLevel(LogManager.getRootLogger().getLevel());
+	}
+
+	public File getFile() {
+		return file;
+	}
+
+	private static org.apache.log4j.Level toLog4jLevel(Level level) {
+		if (level == Level.ALL)
+			return org.apache.log4j.Level.ALL;
+		if (level == Level.TRACE)
+			return org.apache.log4j.Level.TRACE;
+		if (level == Level.DEBUG)
+			return org.apache.log4j.Level.DEBUG;
+		if (level == Level.INFO)
+			return org.apache.log4j.Level.INFO;
+		if (level == Level.WARN)
+			return org.apache.log4j.Level.WARN;
+		if (level == Level.ERROR)
+			return org.apache.log4j.Level.ERROR;
+		// if (level == Level.OFF)
+			return org.apache.log4j.Level.OFF;
+	}
+
+	private static Level fromLog4jLevel(org.apache.log4j.Level level) {
+		if (level == org.apache.log4j.Level.ALL)
+			return Level.ALL;
+		if (level == org.apache.log4j.Level.TRACE)
+			return Level.TRACE;
+		if (level == org.apache.log4j.Level.DEBUG)
+			return Level.DEBUG;
+		if (level == org.apache.log4j.Level.INFO)
+			return Level.INFO;
+		if (level == org.apache.log4j.Level.WARN)
+			return Level.WARN;
+		if (level == org.apache.log4j.Level.ERROR)
+			return Level.ERROR;
+		// if (level == org.apache.log4j.Level.OFF)
+			return Level.OFF;
+	}
+
+    public void setFile(File file) {
+        
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataInput.java
new file mode 100644
index 0000000..68d6a26
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataInput.java
@@ -0,0 +1,98 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize;
+
+import java.io.IOException;
+
+/**
+ * Data input.
+ * 
+ * @author qian.lei
+ */
+public interface DataInput {
+    
+	/**
+	 * Read boolean.
+	 * 
+	 * @return boolean.
+	 * @throws IOException.
+	 */
+	boolean readBool() throws IOException;
+
+	/**
+	 * Read byte.
+	 * 
+	 * @return byte value.
+	 * @throws IOException.
+	 */
+	byte readByte() throws IOException;
+
+	/**
+	 * Read short integer.
+	 * 
+	 * @return short.
+	 * @throws IOException.
+	 */
+	short readShort() throws IOException;
+
+	/**
+	 * Read integer.
+	 * 
+	 * @return integer.
+	 * @throws IOException.
+	 */
+	int readInt() throws IOException;
+
+	/**
+	 * Read long.
+	 * 
+	 * @return long.
+	 * @throws IOException.
+	 */
+	long readLong() throws IOException;
+
+	/**
+	 * Read float.
+	 * 
+	 * @return float.
+	 * @throws IOException.
+	 */
+	float readFloat() throws IOException;
+
+	/**
+	 * Read double.
+	 * 
+	 * @return double.
+	 * @throws IOException.
+	 */
+	double readDouble() throws IOException;
+
+	/**
+	 * Read UTF-8 string.
+	 * 
+	 * @return string.
+	 * @throws IOException.
+	 */
+	String readUTF() throws IOException;
+
+	/**
+	 * Read byte array.
+	 * 
+	 * @return byte array.
+	 * @throws IOException.
+	 */
+	byte[] readBytes() throws IOException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataOutput.java
new file mode 100644
index 0000000..8e5970e
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataOutput.java
@@ -0,0 +1,115 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize;
+
+import java.io.IOException;
+
+/**
+ * Data output.
+ * 
+ * @author qian.lei
+ */
+public interface DataOutput {
+
+	/**
+	 * Write boolean.
+	 * 
+	 * @param v value.
+	 * @throws IOException.
+	 */
+	void writeBool(boolean v) throws IOException;
+
+	/**
+	 * Write byte.
+	 * 
+	 * @param v value.
+	 * @throws IOException.
+	 */
+	void writeByte(byte v) throws IOException;
+
+	/**
+	 * Write short.
+	 * 
+	 * @param v value.
+	 * @throws IOException.
+	 */
+	void writeShort(short v) throws IOException;
+
+	/**
+	 * Write integer.
+	 * 
+	 * @param v value.
+	 * @throws IOException.
+	 */
+	void writeInt(int v) throws IOException;
+
+	/**
+	 * Write long.
+	 * 
+	 * @param v value.
+	 * @throws IOException.
+	 */
+	void writeLong(long v) throws IOException;
+
+	/**
+	 * Write float.
+	 * 
+	 * @param v value.
+	 * @throws IOException.
+	 */
+	void writeFloat(float v) throws IOException;
+
+	/**
+	 * Write double.
+	 * 
+	 * @param v value.
+	 * @throws IOException.
+	 */
+	void writeDouble(double v) throws IOException;
+
+	/**
+	 * Write string.
+	 * 
+	 * @param v value.
+	 * @throws IOException.
+	 */
+	void writeUTF(String v) throws IOException;
+
+	/**
+	 * Write byte array.
+	 * 
+	 * @param v value.
+	 * @throws IOException.
+	 */
+	void writeBytes(byte[] v) throws IOException;
+
+	/**
+	 * Write byte array.
+	 * 
+	 * @param v value.
+	 * @param off offset.
+	 * @param len length.
+	 * @throws IOException.
+	 */
+	void writeBytes(byte[] v, int off, int len) throws IOException;
+
+	/**
+	 * Flush buffer.
+	 * 
+	 * @throws IOException.
+	 */
+	void flushBuffer() throws IOException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectInput.java
new file mode 100644
index 0000000..67a4651
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectInput.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize;
+
+import java.io.IOException;

+import java.lang.reflect.Type;

+
+/**
+ * Object input.
+ * 
+ * @author qian.lei
+ */
+public interface ObjectInput extends DataInput {
+
+	/**
+	 * read object.
+	 * 
+	 * @return object.
+	 */
+	Object readObject() throws IOException, ClassNotFoundException;
+	
+	/**
+	 * read object.
+	 * 
+	 * @param cls object type.
+	 * @return object.
+	 */
+	<T> T readObject(Class<T> cls) throws IOException, ClassNotFoundException;

+	

+	/**

+     * read object.

+     * 

+     * @param cls object type.

+     * @return object.

+     */

+	<T> T readObject(Class<T> cls, Type type) throws IOException, ClassNotFoundException;
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectOutput.java
new file mode 100644
index 0000000..c91d416
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectOutput.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize;
+
+import java.io.IOException;
+
+/**
+ * Object output.
+ * 
+ * @author qian.lei
+ */
+public interface ObjectOutput extends DataOutput {
+
+	/**
+	 * write object.
+	 * 
+	 * @param obj object.
+	 */
+	void writeObject(Object obj) throws IOException;
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Serialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Serialization.java
new file mode 100644
index 0000000..8c8f1d0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Serialization.java
@@ -0,0 +1,69 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * Serialization. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author ding.lid

+ * @author william.liangf

+ */

+@SPI("hessian2")

+public interface Serialization {

+

+    /**

+     * get content type id

+     * 

+     * @return content type id

+     */

+    byte getContentTypeId();

+

+    /**

+     * get content type

+     * 

+     * @return content type

+     */

+    String getContentType();

+

+    /**

+     * create serializer

+     * @param url 

+     * @param output

+     * @return serializer

+     * @throws IOException

+     */

+    @Adaptive

+    ObjectOutput serialize(URL url, OutputStream output) throws IOException;

+

+    /**

+     * create deserializer

+     * @param url 

+     * @param input

+     * @return deserializer

+     * @throws IOException

+     */

+    @Adaptive

+    ObjectInput deserialize(URL url, InputStream input) throws IOException;

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/Builder.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/Builder.java
new file mode 100644
index 0000000..0ffd6f0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/Builder.java
@@ -0,0 +1,1569 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+import java.io.Serializable;

+import java.lang.reflect.Constructor;

+import java.lang.reflect.Field;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.Collection;

+import java.util.Comparator;

+import java.util.Date;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.atomic.AtomicLong;

+import java.util.regex.Matcher;

+

+import com.alibaba.dubbo.common.bytecode.ClassGenerator;

+import com.alibaba.dubbo.common.io.ClassDescriptorMapper;

+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;

+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectInputStream;

+import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectOutputStream;

+import com.alibaba.dubbo.common.utils.ClassHelper;

+import com.alibaba.dubbo.common.utils.IOUtils;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+
+/**
+ * Builder.
+ * 
+ * @author qian.lei
+ *
+ * @param <T> type.
+ */
+
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public abstract class Builder<T> implements GenericDataFlags
+{
+	// Must be protected. by qian.lei
+	protected static Logger logger = LoggerFactory.getLogger(Builder.class);
+
+	private static final AtomicLong BUILDER_CLASS_COUNTER = new AtomicLong(0);
+
+	private static final String BUILDER_CLASS_NAME = Builder.class.getName();
+
+	private static final Map<Class<?>, Builder<?>> BuilderMap = new ConcurrentHashMap<Class<?>, Builder<?>>();
+	private static final Map<Class<?>, Builder<?>> nonSerializableBuilderMap = new ConcurrentHashMap<Class<?>, Builder<?>>();
+
+	private static final String FIELD_CONFIG_SUFFIX = ".fc";
+
+	private static final int MAX_FIELD_CONFIG_FILE_SIZE = 16 * 1024;
+
+	private static final Comparator<String> FNC = new Comparator<String>(){
+		public int compare(String n1, String n2){ return compareFieldName(n1, n2); }
+	};
+
+	private static final Comparator<Field> FC = new Comparator<Field>(){
+		public int compare(Field f1, Field f2){ return compareFieldName(f1.getName(), f2.getName()); }
+	};
+
+	private static final Comparator<Constructor> CC = new Comparator<Constructor>(){
+		public int compare(Constructor o1, Constructor o2){ return o1.getParameterTypes().length - o2.getParameterTypes().length; }
+	};
+
+	// class-descriptor mapper
+	private static final List<String> mDescList = new ArrayList<String>();
+
+	private static final Map<String, Integer> mDescMap = new ConcurrentHashMap<String, Integer>();
+
+	public static ClassDescriptorMapper DEFAULT_CLASS_DESCRIPTOR_MAPPER = new ClassDescriptorMapper(){
+		public String getDescriptor(int index)
+		{
+			if( index < 0 || index >= mDescList.size() )
+				return null;
+			return mDescList.get(index);
+		}
+
+		public int getDescriptorIndex(String desc)
+		{
+			Integer ret = mDescMap.get(desc);
+			return ret == null ? -1 : ret.intValue();
+		}
+	};
+
+	protected Builder(){}
+
+	abstract public Class<T> getType();
+
+	public void writeTo(T obj, OutputStream os) throws IOException
+	{
+		GenericObjectOutput out = new GenericObjectOutput(os);
+		writeTo(obj, out);
+		out.flushBuffer();
+	}
+
+	public T parseFrom(byte[] b) throws IOException
+	{
+		return parseFrom(new UnsafeByteArrayInputStream(b));
+	}
+
+	public T parseFrom(InputStream is) throws IOException
+	{
+		return parseFrom(new GenericObjectInput(is));
+	}
+
+	abstract public void writeTo(T obj, GenericObjectOutput out) throws IOException;
+
+	abstract public T parseFrom(GenericObjectInput in) throws IOException;
+
+	public static <T> Builder<T> register(Class<T> c, boolean isAllowNonSerializable)
+    {
+        if( c == Object.class || c.isInterface() )
+            return (Builder<T>)GenericBuilder;
+        if( c == Object[].class )
+            return (Builder<T>)GenericArrayBuilder;
+
+        Builder<T> b = (Builder<T>)BuilderMap.get(c);
+        if(null != b) return b;
+        
+        boolean isSerializable = Serializable.class.isAssignableFrom(c);
+        if(!isAllowNonSerializable && !isSerializable) {
+            throw new IllegalStateException("Serialized class " + c.getName() +
+            " must implement java.io.Serializable (dubbo codec setting: isAllowNonSerializable = false)");
+        }
+        
+        b = (Builder<T>)nonSerializableBuilderMap.get(c);
+        if(null != b) return b;
+        
+        b = newBuilder(c);
+        if(isSerializable)
+            BuilderMap.put(c, b);
+        else
+            nonSerializableBuilderMap.put(c, b);
+        
+        return b;
+    } 
+	
+	public static <T> Builder<T> register(Class<T> c)
+	{
+	    return register(c, false);
+	}
+
+	public static <T> void register(Class<T> c, Builder<T> b)
+	{
+	    if(Serializable.class.isAssignableFrom(c))
+	        BuilderMap.put(c, b);
+	    else
+	        nonSerializableBuilderMap.put(c, b);
+	}
+
+	private static <T> Builder<T> newBuilder(Class<T> c)
+	{
+		if( c.isPrimitive() )
+			throw new RuntimeException("Can not create builder for primitive type: " + c);
+
+		if( logger.isInfoEnabled() )
+			logger.info("create Builder for class: " + c);
+
+		Builder<?> builder;
+		if( c.isArray() )
+			builder = newArrayBuilder(c);
+		else
+			builder = newObjectBuilder(c);
+		return (Builder<T>)builder;
+	}
+	
+	private static Builder<?> newArrayBuilder(Class<?> c)
+	{
+		Class<?> cc = c.getComponentType();
+		if( cc.isInterface() )
+			return GenericArrayBuilder;
+
+		ClassLoader cl = ClassHelper.getClassLoader(c);
+
+		String cn = ReflectUtils.getName(c), ccn = ReflectUtils.getName(cc); // get class name as int[][], double[].
+		String bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
+
+		int ix = cn.indexOf(']');
+		String s1 = cn.substring(0, ix), s2 = cn.substring(ix); // if name='int[][]' then s1='int[', s2='][]'
+
+		StringBuilder cwt = new StringBuilder("public void writeTo(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{"); // writeTo code.
+		StringBuilder cpf = new StringBuilder("public Object parseFrom(").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{"); // parseFrom code.
+
+		cwt.append("if( $1 == null ){ $2.write0(OBJECT_NULL); return; }");
+		cwt.append(cn).append(" v = (").append(cn).append(")$1; int len = v.length; $2.write0(OBJECT_VALUES); $2.writeUInt(len); for(int i=0;i<len;i++){ ");
+
+		cpf.append("byte b = $1.read0(); if( b == OBJECT_NULL ) return null; if( b != OBJECT_VALUES ) throw new java.io.IOException(\"Input format error, expect OBJECT_NULL|OBJECT_VALUES, get \" + b + \".\");");
+		cpf.append("int len = $1.readUInt(); if( len == 0 ) return new ").append(s1).append('0').append(s2).append("; ");
+		cpf.append(cn).append(" ret = new ").append(s1).append("len").append(s2).append("; for(int i=0;i<len;i++){ ");
+
+		Builder<?> builder = null;
+		if( cc.isPrimitive() )
+		{
+			if( cc == boolean.class )
+			{
+				cwt.append("$2.writeBool(v[i]);");
+				cpf.append("ret[i] = $1.readBool();");
+			}
+			else if( cc == byte.class )
+			{
+				cwt.append("$2.writeByte(v[i]);");
+				cpf.append("ret[i] = $1.readByte();");
+			}
+			else if( cc == char.class )
+			{
+				cwt.append("$2.writeShort((short)v[i]);");
+				cpf.append("ret[i] = (char)$1.readShort();");
+			}
+			else if( cc == short.class )
+			{
+				cwt.append("$2.writeShort(v[i]);");
+				cpf.append("ret[i] = $1.readShort();");
+			}
+			else if( cc == int.class )
+			{
+				cwt.append("$2.writeInt(v[i]);");
+				cpf.append("ret[i] = $1.readInt();");
+			}
+			else if( cc == long.class )
+			{
+				cwt.append("$2.writeLong(v[i]);");
+				cpf.append("ret[i] = $1.readLong();");
+			}
+			else if( cc == float.class )
+			{
+				cwt.append("$2.writeFloat(v[i]);");
+				cpf.append("ret[i] = $1.readFloat();");
+			}
+			else if( cc == double.class )
+			{
+				cwt.append("$2.writeDouble(v[i]);");
+				cpf.append("ret[i] = $1.readDouble();");
+			}
+		}
+		else
+		{
+			builder = register(cc);
+
+			cwt.append("builder.writeTo(v[i], $2);");
+			cpf.append("ret[i] = (").append(ccn).append(")builder.parseFrom($1);");
+		}
+		cwt.append(" } }");
+		cpf.append(" } return ret; }");
+
+		ClassGenerator cg = ClassGenerator.newInstance(cl);
+		cg.setClassName(bcn);
+		cg.setSuperClass(Builder.class);
+		cg.addDefaultConstructor();
+		if( builder != null )
+			cg.addField("public static " + BUILDER_CLASS_NAME + " builder;");
+		cg.addMethod("public Class getType(){ return " + cn + ".class; }");
+		cg.addMethod(cwt.toString());
+		cg.addMethod(cpf.toString());
+		try
+		{
+			Class<?> wc = cg.toClass();
+			// set static field.
+			if( builder != null )
+				wc.getField("builder").set(null, builder);
+			return (Builder<?>)wc.newInstance();
+		}
+		catch(RuntimeException e)
+		{
+			throw e;
+		}
+		catch(Throwable e)
+		{
+			throw new RuntimeException(e.getMessage());
+		}
+		finally
+		{
+			cg.release();
+		}
+	}
+
+	private static Builder<?> newObjectBuilder(final Class<?> c)
+	{
+		if( c.isEnum() )
+			return newEnumBuilder(c);
+
+		if( c.isAnonymousClass() )
+			throw new RuntimeException("Can not instantiation anonymous class: " + c);
+
+		if( c.getEnclosingClass() != null && !Modifier.isStatic(c.getModifiers()) )
+			throw new RuntimeException("Can not instantiation inner and non-static class: " + c);
+
+		if( Throwable.class.isAssignableFrom(c) )
+			return SerializableBuilder;
+
+		ClassLoader cl = ClassHelper.getClassLoader(c);
+	
+		// is same package.
+		boolean isp;
+		String cn = c.getName(), bcn;
+		if( c.getClassLoader() == null ) // is system class. if( cn.startsWith("java.") || cn.startsWith("javax.") || cn.startsWith("sun.") )
+		{
+			isp = false;
+			bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
+		}
+		else
+		{
+			isp = true;
+			bcn = cn + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
+		}
+
+		// is Collection, is Map, is Serializable.
+		boolean isc = Collection.class.isAssignableFrom(c);
+		boolean ism = !isc && Map.class.isAssignableFrom(c);
+		boolean iss = !( isc || ism ) && Serializable.class.isAssignableFrom(c);
+
+		// deal with fields.
+		String[] fns = null; // fix-order fields names
+		InputStream is = c.getResourceAsStream(c.getSimpleName() + FIELD_CONFIG_SUFFIX); // load field-config file.
+		if( is != null )
+		{
+			try
+			{
+				int len = is.available();
+				if( len > 0 )
+				{
+					if( len > MAX_FIELD_CONFIG_FILE_SIZE )
+						throw new RuntimeException("Load [" + c.getName() + "] field-config file error: File-size too larger");
+
+					String[] lines = IOUtils.readLines(is);
+					if( lines != null && lines.length > 0 )
+					{
+						List<String> list = new ArrayList<String>();
+						for(int i=0;i<lines.length;i++)
+						{
+							fns = lines[i].split(",");
+							Arrays.sort(fns, FNC);
+							for(int j=0;j<fns.length;j++)
+								list.add(fns[j]);
+						}
+						fns = list.toArray(new String[0]);
+					}
+				}
+			}
+			catch(IOException e)
+			{
+				throw new RuntimeException("Load [" + c.getName() + "] field-config file error: " + e.getMessage() );
+			}
+			finally
+			{
+				try{ is.close(); }
+				catch(IOException e){}
+			}
+		}
+
+		Field f, fs[];
+		if( fns != null )
+		{
+			fs = new Field[fns.length];
+			for(int i=0;i<fns.length;i++)
+			{
+				String fn = fns[i];
+				try
+				{
+					f = c.getDeclaredField(fn);
+					int mod = f.getModifiers();
+					if( Modifier.isStatic(mod) || (serializeIgnoreFinalModifier(c) && Modifier.isFinal(mod)) )
+						throw new RuntimeException("Field [" + c.getName() + "." + fn + "] is static/final field.");
+					if( Modifier.isTransient(mod) )
+					{
+						if( iss )
+							return SerializableBuilder;
+						throw new RuntimeException("Field [" + c.getName() + "." + fn + "] is transient field.");
+					}
+					f.setAccessible(true);
+					fs[i] = f;
+				}
+				catch(SecurityException e)
+				{
+					throw new RuntimeException(e.getMessage());
+				}
+				catch(NoSuchFieldException e)
+				{
+					throw new RuntimeException("Field [" + c.getName() + "." + fn + "] not found.");
+				}
+			}
+		}
+		else
+		{
+			Class<?> t = c;
+			List<Field> fl = new ArrayList<Field>();
+			do
+			{
+				fs = t.getDeclaredFields();
+				for( Field tf : fs )
+				{
+					int mod = tf.getModifiers();
+                    if (Modifier.isStatic(mod)

+                            || (serializeIgnoreFinalModifier(c) && Modifier.isFinal(mod))

+                            || tf.getName().equals("this$0") // skip static or inner-class's 'this$0' field.

+                            || ! Modifier.isPublic(tf.getType().getModifiers()) ) //skip private inner-class field
+						continue;
+					if( Modifier.isTransient(mod) )
+					{
+						if( iss )
+							return SerializableBuilder;
+						continue;
+					}
+					tf.setAccessible(true);
+					fl.add(tf);
+				}
+				t = t.getSuperclass();
+			}
+			while( t != Object.class );
+
+			fs = fl.toArray(new Field[0]);
+			if( fs.length > 1 )
+				Arrays.sort(fs, FC);
+		}
+
+		// deal with constructors.
+		Constructor<?>[] cs = c.getDeclaredConstructors();
+		if( cs.length == 0 )
+		{
+			Class<?> t = c;
+			do
+			{
+				t = t.getSuperclass();
+				if( t == null )
+					throw new RuntimeException("Can not found Constructor?");
+				cs = c.getDeclaredConstructors();
+			}
+			while( cs.length == 0 );
+		}
+		if( cs.length > 1 )
+			Arrays.sort(cs, CC);
+
+		// writeObject code.
+		StringBuilder cwf = new StringBuilder("protected void writeObject(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{");
+		cwf.append(cn).append(" v = (").append(cn).append(")$1; ");
+		cwf.append("$2.writeInt(fields.length);");
+
+		// readObject code.
+		StringBuilder crf = new StringBuilder("protected void readObject(Object ret, ").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{");
+		crf.append("int fc = $2.readInt();");
+		crf.append("if( fc != ").append(fs.length).append(" ) throw new IllegalStateException(\"Deserialize Class [").append(cn).append("], field count not matched. Expect ").append(fs.length).append(" but get \" + fc +\".\");");
+		crf.append(cn).append(" ret = (").append(cn).append(")$1;");
+
+		// newInstance code.
+		StringBuilder cni = new StringBuilder("protected Object newInstance(").append(GenericObjectInput.class.getName()).append(" in){ return ");
+		Constructor<?> con = cs[0];
+		int mod = con.getModifiers();
+		boolean dn = Modifier.isPublic(mod) || ( isp && !Modifier.isPrivate(mod) );
+		if( dn )
+		{
+			cni.append("new ").append(cn).append("(");
+		}
+		else
+		{
+			con.setAccessible(true);
+			cni.append("constructor.newInstance(new Object[]{");
+		}
+		Class<?>[] pts = con.getParameterTypes();
+		for(int i=0;i<pts.length;i++)
+		{
+			if( i > 0 )
+				cni.append(',');
+			cni.append(defaultArg(pts[i]));
+		}
+		if( !dn )
+			cni.append("}"); // close object array.
+		cni.append("); }");
+
+		// get bean-style property metadata.
+		Map<String, PropertyMetadata> pms = propertyMetadatas(c);
+		List<Builder<?>> builders = new ArrayList<Builder<?>>(fs.length);
+		String fn, ftn; // field name, field type name.
+		Class<?> ft; // field type.
+		boolean da; // direct access.
+		PropertyMetadata pm;
+		for(int i=0;i<fs.length;i++)
+		{
+			f = fs[i];
+			fn = f.getName();
+			ft = f.getType();
+			ftn = ReflectUtils.getName(ft);
+			da = isp && ( f.getDeclaringClass() == c ) && ( Modifier.isPrivate(f.getModifiers()) == false );
+			if( da )
+			{
+				pm = null;
+			}
+			else
+			{
+				pm = pms.get(fn);
+				if( pm != null && ( pm.type != ft || pm.setter == null || pm.getter == null ) )
+					pm = null;
+			}
+
+			crf.append("if( fc == ").append(i).append(" ) return;");
+			if( ft.isPrimitive() )
+			{
+				if( ft == boolean.class )
+				{
+					if( da )
+					{
+						cwf.append("$2.writeBool(v.").append(fn).append(");");
+						crf.append("ret.").append(fn).append(" = $2.readBool();");
+					}
+					else if( pm != null )
+					{
+						cwf.append("$2.writeBool(v.").append(pm.getter).append("());");
+						crf.append("ret.").append(pm.setter).append("($2.readBool());");
+					}
+					else
+					{
+						cwf.append("$2.writeBool(((Boolean)fields[").append(i).append("].get($1)).booleanValue());");
+						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readBool());");
+					}
+				}
+				else if( ft == byte.class )
+				{
+					if( da )
+					{
+						cwf.append("$2.writeByte(v.").append(fn).append(");");
+						crf.append("ret.").append(fn).append(" = $2.readByte();");
+					}
+					else if( pm != null )
+					{
+						cwf.append("$2.writeByte(v.").append(pm.getter).append("());");
+						crf.append("ret.").append(pm.setter).append("($2.readByte());");
+					}
+					else
+					{
+						cwf.append("$2.writeByte(((Byte)fields[").append(i).append("].get($1)).byteValue());");
+						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readByte());");
+					}
+				}
+				else if( ft == char.class )
+				{
+					if( da )
+					{
+						cwf.append("$2.writeShort((short)v.").append(fn).append(");");
+						crf.append("ret.").append(fn).append(" = (char)$2.readShort();");
+					}
+					else if( pm != null )
+					{
+						cwf.append("$2.writeShort((short)v.").append(pm.getter).append("());");
+						crf.append("ret.").append(pm.setter).append("((char)$2.readShort());");
+					}
+					else
+					{
+						cwf.append("$2.writeShort((short)((Character)fields[").append(i).append("].get($1)).charValue());");
+						crf.append("fields[").append(i).append("].set(ret, ($w)((char)$2.readShort()));");
+					}
+				}
+				else if( ft == short.class )
+				{
+					if( da )
+					{
+						cwf.append("$2.writeShort(v.").append(fn).append(");");
+						crf.append("ret.").append(fn).append(" = $2.readShort();");
+					}
+					else if( pm != null )
+					{
+						cwf.append("$2.writeShort(v.").append(pm.getter).append("());");
+						crf.append("ret.").append(pm.setter).append("($2.readShort());");
+					}
+					else
+					{
+						cwf.append("$2.writeShort(((Short)fields[").append(i).append("].get($1)).shortValue());");
+						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readShort());");
+					}
+				}
+				else if( ft == int.class )
+				{
+					if( da )
+					{
+						cwf.append("$2.writeInt(v.").append(fn).append(");");
+						crf.append("ret.").append(fn).append(" = $2.readInt();");
+					}
+					else if( pm != null )
+					{
+						cwf.append("$2.writeInt(v.").append(pm.getter).append("());");
+						crf.append("ret.").append(pm.setter).append("($2.readInt());");
+					}
+					else
+					{
+						cwf.append("$2.writeInt(((Integer)fields[").append(i).append("].get($1)).intValue());");
+						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readInt());");
+					}
+				}
+				else if( ft == long.class )
+				{
+					if( da )
+					{
+						cwf.append("$2.writeLong(v.").append(fn).append(");");
+						crf.append("ret.").append(fn).append(" = $2.readLong();");
+					}
+					else if( pm != null )
+					{
+						cwf.append("$2.writeLong(v.").append(pm.getter).append("());");
+						crf.append("ret.").append(pm.setter).append("($2.readLong());");
+					}
+					else
+					{
+						cwf.append("$2.writeLong(((Long)fields[").append(i).append("].get($1)).longValue());");
+						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readLong());");
+					}
+				}
+				else if( ft == float.class )
+				{
+					if( da )
+					{
+						cwf.append("$2.writeFloat(v.").append(fn).append(");");
+						crf.append("ret.").append(fn).append(" = $2.readFloat();");
+					}
+					else if( pm != null )
+					{
+						cwf.append("$2.writeFloat(v.").append(pm.getter).append("());");
+						crf.append("ret.").append(pm.setter).append("($2.readFloat());");
+					}
+					else
+					{
+						cwf.append("$2.writeFloat(((Float)fields[").append(i).append("].get($1)).floatValue());");
+						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readFloat());");
+					}
+				}
+				else if( ft == double.class )
+				{
+					if( da )
+					{
+						cwf.append("$2.writeDouble(v.").append(fn).append(");");
+						crf.append("ret.").append(fn).append(" = $2.readDouble();");
+					}
+					else if( pm != null )
+					{
+						cwf.append("$2.writeDouble(v.").append(pm.getter).append("());");
+						crf.append("ret.").append(pm.setter).append("($2.readDouble());");
+					}
+					else
+					{
+						cwf.append("$2.writeDouble(((Double)fields[").append(i).append("].get($1)).doubleValue());");
+						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readDouble());");
+					}
+				}
+			}
+			else if( ft == c )
+			{
+				if( da )
+				{
+					cwf.append("this.writeTo(v.").append(fn).append(", $2);");
+					crf.append("ret.").append(fn).append(" = (").append(ftn).append(")this.parseFrom($2);");
+				}
+				else if( pm != null )
+				{
+					cwf.append("this.writeTo(v.").append(pm.getter).append("(), $2);");
+					crf.append("ret.").append(pm.setter).append("((").append(ftn).append(")this.parseFrom($2));");
+				}
+				else
+				{
+					cwf.append("this.writeTo((").append(ftn).append(")fields[").append(i).append("].get($1), $2);");
+					crf.append("fields[").append(i).append("].set(ret, this.parseFrom($2));");
+				}
+			}
+			else
+			{
+				int bc = builders.size();
+				builders.add( register(ft) );
+
+				if( da )
+				{
+					cwf.append("builders[").append(bc).append("].writeTo(v.").append(fn).append(", $2);");
+					crf.append("ret.").append(fn).append(" = (").append(ftn).append(")builders[").append(bc).append("].parseFrom($2);");
+				}
+				else if( pm != null )
+				{
+					cwf.append("builders[").append(bc).append("].writeTo(v.").append(pm.getter).append("(), $2);");
+					crf.append("ret.").append(pm.setter).append("((").append(ftn).append(")builders[").append(bc).append("].parseFrom($2));");
+				}
+				else
+				{
+					cwf.append("builders[").append(bc).append("].writeTo((").append(ftn).append(")fields[").append(i).append("].get($1), $2);");
+					crf.append("fields[").append(i).append("].set(ret, builders[").append(bc).append("].parseFrom($2));");
+				}
+			}
+		}
+
+		// skip any fields.
+		crf.append("for(int i=").append(fs.length).append(";i<fc;i++) $2.skipAny();");
+
+		// collection or map
+		if( isc )
+		{
+			cwf.append("$2.writeInt(v.size()); for(java.util.Iterator it=v.iterator();it.hasNext();){ $2.writeObject(it.next()); }");
+			crf.append("int len = $2.readInt(); for(int i=0;i<len;i++) ret.add($2.readObject());");
+		}
+		else if( ism )
+		{
+			cwf.append("$2.writeInt(v.size()); for(java.util.Iterator it=v.entrySet().iterator();it.hasNext();){ java.util.Map.Entry entry = (java.util.Map.Entry)it.next(); $2.writeObject(entry.getKey()); $2.writeObject(entry.getValue()); }");
+			crf.append("int len = $2.readInt(); for(int i=0;i<len;i++) ret.put($2.readObject(), $2.readObject());");
+		}
+		cwf.append(" }");
+		crf.append(" }");
+
+		ClassGenerator cg = ClassGenerator.newInstance(cl);
+		cg.setClassName(bcn);
+		cg.setSuperClass(AbstractObjectBuilder.class);
+		cg.addDefaultConstructor();
+		cg.addField("public static java.lang.reflect.Field[] fields;");
+		cg.addField("public static " + BUILDER_CLASS_NAME + "[] builders;");
+		if( !dn )
+			cg.addField("public static java.lang.reflect.Constructor constructor;");
+		cg.addMethod("public Class getType(){ return " + cn + ".class; }");
+		cg.addMethod(cwf.toString());
+		cg.addMethod(crf.toString());
+		cg.addMethod(cni.toString());
+		try
+		{
+			Class<?> wc = cg.toClass();
+			// set static field
+			wc.getField("fields").set(null, fs);
+			wc.getField("builders").set(null, builders.toArray(new Builder<?>[0]));
+			if( !dn )
+				wc.getField("constructor").set(null, con);
+			return (Builder<?>)wc.newInstance();
+		}
+		catch(RuntimeException e)
+		{
+			throw e;
+		}
+		catch(Throwable e)
+		{
+			throw new RuntimeException(e.getMessage(), e);
+		}
+		finally
+		{
+			cg.release();
+		}
+	}
+
+	private static Builder<?> newEnumBuilder(Class<?> c)
+	{
+		ClassLoader cl = ClassHelper.getClassLoader(c);
+		
+		String cn = c.getName();
+		String bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
+
+		StringBuilder cwt = new StringBuilder("public void writeTo(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{"); // writeTo code.
+		cwt.append(cn).append(" v = (").append(cn).append(")$1;");
+		cwt.append("if( $1 == null ){ $2.writeUTF(null); }else{ $2.writeUTF(v.name()); } }");
+
+		StringBuilder cpf = new StringBuilder("public Object parseFrom(").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{"); // parseFrom code.
+		cpf.append("String name = $1.readUTF(); if( name == null ) return null; return (").append(cn).append(")Enum.valueOf(").append(cn).append(".class, name); }");
+
+		ClassGenerator cg = ClassGenerator.newInstance(cl);
+		cg.setClassName(bcn);
+		cg.setSuperClass(Builder.class);
+		cg.addDefaultConstructor();
+		cg.addMethod("public Class getType(){ return " + cn + ".class; }");
+		cg.addMethod(cwt.toString());
+		cg.addMethod(cpf.toString());
+		try
+		{
+			Class<?> wc = cg.toClass();
+			return (Builder<?>)wc.newInstance();
+		}
+		catch(RuntimeException e)
+		{
+			throw e;
+		}
+		catch(Throwable e)
+		{
+			throw new RuntimeException(e.getMessage(), e);
+		}
+		finally
+		{
+			cg.release();
+		}
+	}
+
+	private static Map<String, PropertyMetadata> propertyMetadatas(Class<?> c)
+	{
+		Map<String, Method> mm = new HashMap<String, Method>(); // method map.
+		Map<String, PropertyMetadata> ret = new HashMap<String, PropertyMetadata>(); // property metadata map.
+
+		// All public method.
+		for( Method m : c.getMethods() )
+		{
+			if( m.getDeclaringClass() == Object.class ) // Ignore Object's method.
+				continue;
+			mm.put(ReflectUtils.getDesc(m), m);
+		}
+
+		Matcher matcher;
+		for( Map.Entry<String,Method> entry : mm.entrySet() )
+		{
+			String desc = entry.getKey();
+			Method method = entry.getValue();
+			if( ( matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(desc) ).matches() ||
+					( matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(desc) ).matches() )
+			{
+				String pn = propertyName(matcher.group(1));
+				Class<?> pt = method.getReturnType();
+				PropertyMetadata pm = ret.get(pn);
+				if( pm == null )
+				{
+					pm = new PropertyMetadata();
+					pm.type = pt;
+					ret.put(pn, pm);
+				}
+				else
+				{
+					if( pm.type != pt )
+						continue;
+				}
+				pm.getter = method.getName();
+			}
+			else if( ( matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(desc) ).matches() )
+			{
+				String pn = propertyName(matcher.group(1));
+				Class<?> pt = method.getParameterTypes()[0];
+				PropertyMetadata pm = ret.get(pn);
+				if( pm == null )
+				{
+					pm = new PropertyMetadata();
+					pm.type = pt;
+					ret.put(pn, pm);
+				}
+				else
+				{
+					if( pm.type != pt )
+						continue;
+				}
+				pm.setter = method.getName();
+			}
+		}
+		return ret;
+	}
+
+	private static String propertyName(String s)
+	{
+		return s.length() == 1 || Character.isLowerCase(s.charAt(1)) ? Character.toLowerCase(s.charAt(0)) + s.substring(1) : s;
+	}

+	

+	private static boolean serializeIgnoreFinalModifier(Class cl)

+    {

+//	    if (cl.isAssignableFrom(BigInteger.class)) return false;

+//	    for performance

+//	    if (cl.getName().startsWith("java")) return true;

+//	    if (cl.getName().startsWith("javax")) return true;

+	    

+	    return false;

+    }

+	

+	@SuppressWarnings("unused")

+    private static boolean isPrimitiveOrPrimitiveArray1(Class<?> cl)

+    {

+        if (cl.isPrimitive()){

+            return true;

+        } else {

+            Class clazz = cl.getClass().getComponentType();

+            if (clazz!=null && clazz.isPrimitive()){

+                return true;

+            }

+        } 

+        return false;

+    }

+
+	private static String defaultArg(Class<?> cl)
+	{
+	    if( boolean.class == cl ) return "false";
+	    if( int.class == cl ) return "0";
+	    if( long.class == cl ) return "0l";
+	    if( double.class == cl ) return "(double)0";
+	    if( float.class == cl ) return "(float)0";
+	    if( short.class == cl ) return "(short)0";
+	    if( char.class == cl ) return "(char)0";
+	    if( byte.class == cl ) return "(byte)0";

+	    if( byte[].class == cl ) return "new byte[]{0}";

+	    if( !cl.isPrimitive() ) return "null";
+	    throw new UnsupportedOperationException();
+	}
+
+	private static int compareFieldName(String n1, String n2)
+	{
+		int l = Math.min(n1.length(), n2.length());
+		for(int i=0;i<l;i++)
+		{
+			int t = n1.charAt(i) - n2.charAt(i);
+			if( t != 0 )
+				return t;
+		}
+		return n1.length() - n2.length();
+	}
+
+	private static void addDesc(Class<?> c)
+	{
+		String desc = ReflectUtils.getDesc(c);
+		int index = mDescList.size();
+		mDescList.add(desc);
+		mDescMap.put(desc, index);
+	}
+
+	static class PropertyMetadata
+	{
+		Class<?> type;
+		String setter, getter;
+	}
+
+	public static abstract class AbstractObjectBuilder<T> extends Builder<T>
+	{
+		abstract public Class<T> getType();
+
+		public void writeTo(T obj, GenericObjectOutput out) throws IOException
+		{
+			if( obj == null )
+			{
+				out.write0(OBJECT_NULL);
+			}
+			else
+			{
+				int ref = out.getRef(obj);
+				if( ref < 0 )
+				{
+					out.addRef(obj);
+					out.write0(OBJECT);
+					writeObject(obj, out);
+				}
+				else
+				{
+					out.write0(OBJECT_REF);
+					out.writeUInt(ref);
+				}
+			}
+		}
+
+		public T parseFrom(GenericObjectInput in) throws IOException
+		{
+			byte b = in.read0();
+			switch( b )
+			{
+				case OBJECT:
+				{
+					T ret = newInstance(in);
+					in.addRef(ret);
+					readObject(ret, in);
+					return ret;
+				}
+				case OBJECT_REF:
+					return (T)in.getRef(in.readUInt());
+				case OBJECT_NULL:
+					return null;
+				default:
+					throw new IOException("Input format error, expect OBJECT|OBJECT_REF|OBJECT_NULL, get " + b);
+			}
+		}
+
+		abstract protected void writeObject(T obj, GenericObjectOutput out) throws IOException;
+
+		abstract protected T newInstance(GenericObjectInput in) throws IOException;
+
+		abstract protected void readObject(T ret, GenericObjectInput in) throws IOException;
+	}
+
+	static final Builder<Object> GenericBuilder = new Builder<Object>(){
+		@Override
+		public Class<Object> getType(){ return Object.class; }
+		@Override
+		public void writeTo(Object obj, GenericObjectOutput out) throws IOException{ out.writeObject(obj); }
+		@Override
+		public Object parseFrom(GenericObjectInput in) throws IOException{ return in.readObject(); }
+	};
+
+	static final Builder<Object[]> GenericArrayBuilder = new AbstractObjectBuilder<Object[]>(){
+		@Override
+		public Class<Object[]> getType(){ return Object[].class; }
+		@Override
+		protected Object[] newInstance(GenericObjectInput in) throws IOException
+		{
+			return new Object[in.readUInt()];
+		}
+		@Override
+		protected void readObject(Object[] ret, GenericObjectInput in) throws IOException
+		{
+			for(int i=0;i<ret.length;i++)
+				ret[i] = in.readObject();
+		}
+		@Override
+		protected void writeObject(Object[] obj, GenericObjectOutput out) throws IOException
+		{
+			out.writeUInt(obj.length);
+			for( Object item : obj )
+				out.writeObject(item);
+		}
+	};
+
+	static final Builder<Serializable> SerializableBuilder = new Builder<Serializable>(){
+		@Override
+		public Class<Serializable> getType()
+		{
+			return Serializable.class;
+		}
+		@Override
+		public void writeTo(Serializable obj, GenericObjectOutput out) throws IOException
+		{
+			if( obj == null )
+			{
+				out.write0(OBJECT_NULL);
+			}
+			else
+			{
+				out.write0(OBJECT_STREAM);
+				UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
+				CompactedObjectOutputStream oos = new CompactedObjectOutputStream(bos);
+				oos.writeObject(obj);
+				oos.flush();
+				bos.close();
+				byte[] b = bos.toByteArray();
+				out.writeUInt(b.length);
+				out.write0(b, 0, b.length);
+			}
+		}
+		@Override
+		public Serializable parseFrom(GenericObjectInput in) throws IOException
+		{
+			byte b = in.read0();
+			if( b == OBJECT_NULL )
+				return null;
+			if( b != OBJECT_STREAM )
+				throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_STREAM, get " + b + ".");
+
+			UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(in.read0(in.readUInt()));
+			CompactedObjectInputStream ois = new CompactedObjectInputStream(bis);
+			try{ return (Serializable)ois.readObject(); }
+			catch(ClassNotFoundException e){ throw new IOException(StringUtils.toString(e)); }
+		}
+	};
+
+	static
+	{
+		addDesc(boolean[].class);
+		addDesc(byte[].class);
+		addDesc(char[].class);
+		addDesc(short[].class);
+		addDesc(int[].class);
+		addDesc(long[].class);
+		addDesc(float[].class);
+		addDesc(double[].class);
+
+		addDesc(Boolean.class);
+		addDesc(Byte.class);
+		addDesc(Character.class);
+		addDesc(Short.class);
+		addDesc(Integer.class);
+		addDesc(Long.class);
+		addDesc(Float.class);
+		addDesc(Double.class);
+
+		addDesc(String.class);
+		addDesc(String[].class);
+
+		addDesc(ArrayList.class);
+		addDesc(HashMap.class);
+		addDesc(HashSet.class);
+		addDesc(Date.class);
+		addDesc(java.sql.Date.class);
+		addDesc(java.sql.Time.class);
+		addDesc(java.sql.Timestamp.class);
+		addDesc(java.util.LinkedList.class);
+		addDesc(java.util.LinkedHashMap.class);
+		addDesc(java.util.LinkedHashSet.class);
+
+		register(byte[].class, new Builder<byte[]>(){
+			@Override
+			public Class<byte[]> getType(){ return byte[].class; }
+			@Override
+			public void writeTo(byte[] obj, GenericObjectOutput out) throws IOException{ out.writeBytes(obj); }
+			@Override
+			public byte[] parseFrom(GenericObjectInput in) throws IOException{ return in.readBytes(); }
+		});
+		register(Boolean.class, new Builder<Boolean>(){
+			@Override
+			public Class<Boolean> getType(){ return Boolean.class; }
+			@Override
+			public void writeTo(Boolean obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+					out.write0(VARINT_N1);
+				else if( obj.booleanValue() )
+					out.write0(VARINT_1);
+				else
+					out.write0(VARINT_0);
+			}
+			@Override
+			public Boolean parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				switch( b )
+				{
+					case VARINT_N1: return null;
+					case VARINT_0: return Boolean.FALSE;
+					case VARINT_1: return Boolean.TRUE;
+					default: throw new IOException("Input format error, expect VARINT_N1|VARINT_0|VARINT_1, get " + b + ".");
+				}
+			}
+		});
+		register(Byte.class, new Builder<Byte>(){
+			@Override
+			public Class<Byte> getType(){ return Byte.class; }
+			@Override
+			public void writeTo(Byte obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeByte(obj.byteValue());
+				}
+			}
+			@Override
+			public Byte parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return Byte.valueOf(in.readByte());
+			}
+		});
+		register(Character.class, new Builder<Character>(){
+			@Override
+			public Class<Character> getType(){ return Character.class; }
+			@Override
+			public void writeTo(Character obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeShort((short)obj.charValue());
+				}
+			}
+			@Override
+			public Character parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return Character.valueOf((char)in.readShort());
+			}
+		});
+		register(Short.class, new Builder<Short>(){
+			@Override
+			public Class<Short> getType(){ return Short.class; }
+			@Override
+			public void writeTo(Short obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeShort(obj.shortValue());
+				}
+			}
+			@Override
+			public Short parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return Short.valueOf(in.readShort());
+			}
+		});
+		register(Integer.class, new Builder<Integer>(){
+			@Override
+			public Class<Integer> getType(){ return Integer.class; }
+			@Override
+			public void writeTo(Integer obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeInt(obj.intValue());
+				}
+			}
+			@Override
+			public Integer parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return Integer.valueOf(in.readInt());
+			}
+		});
+		register(Long.class, new Builder<Long>(){
+			@Override
+			public Class<Long> getType(){ return Long.class; }
+			@Override
+			public void writeTo(Long obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeLong(obj.longValue());
+				}
+			}
+			@Override
+			public Long parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return Long.valueOf(in.readLong());
+			}
+		});
+		register(Float.class, new Builder<Float>(){
+			@Override
+			public Class<Float> getType(){ return Float.class; }
+			@Override
+			public void writeTo(Float obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeFloat(obj.floatValue());
+				}
+			}
+			@Override
+			public Float parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return new Float(in.readFloat());
+			}
+		});
+		register(Double.class, new Builder<Double>(){
+			@Override
+			public Class<Double> getType(){ return Double.class; }
+			@Override
+			public void writeTo(Double obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeDouble(obj.doubleValue());
+				}
+			}
+			@Override
+			public Double parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return new Double(in.readDouble());
+			}
+		});
+		register(String.class, new Builder<String>(){
+			@Override
+			public Class<String> getType(){ return String.class; }
+			@Override
+			public String parseFrom(GenericObjectInput in) throws IOException{ return in.readUTF(); }
+			@Override
+			public void writeTo(String obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj); }
+		});
+		register(StringBuilder.class, new Builder<StringBuilder>(){
+			@Override
+			public Class<StringBuilder> getType(){ return StringBuilder.class; }
+			@Override
+			public StringBuilder parseFrom(GenericObjectInput in) throws IOException{ return new StringBuilder(in.readUTF()); }
+			@Override
+			public void writeTo(StringBuilder obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); }
+		});
+		register(StringBuffer.class, new Builder<StringBuffer>(){
+			@Override
+			public Class<StringBuffer> getType(){ return StringBuffer.class; }
+			@Override
+			public StringBuffer parseFrom(GenericObjectInput in) throws IOException{ return new StringBuffer(in.readUTF()); }
+			@Override
+			public void writeTo(StringBuffer obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); }
+		});
+
+		// java.util
+		register(ArrayList.class, new Builder<ArrayList>(){
+			@Override
+			public Class<ArrayList> getType(){ return ArrayList.class; }
+			@Override
+			public void writeTo(ArrayList obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUES);
+					out.writeUInt(obj.size());
+					for( Object item : obj )
+						out.writeObject(item);
+				}
+			}
+			@Override
+			public ArrayList parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUES )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + ".");
+
+				int len = in.readUInt();
+				ArrayList ret = new ArrayList(len);
+				for(int i=0;i<len;i++)
+					ret.add(in.readObject());
+				return ret;
+			}
+		});
+		register(HashMap.class, new Builder<HashMap>(){
+			@Override
+			public Class<HashMap> getType(){ return HashMap.class; }
+			@Override
+			public void writeTo(HashMap obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_MAP);
+					out.writeUInt(obj.size());
+					for( Map.Entry entry : (Set<Map.Entry>)obj.entrySet() )
+					{
+						out.writeObject(entry.getKey());
+						out.writeObject(entry.getValue());
+					}
+				}
+			}
+			@Override
+			public HashMap parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_MAP )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_MAP, get " + b + ".");
+
+				int len = in.readUInt();
+				HashMap ret = new HashMap(len);
+				for(int i=0;i<len;i++)
+					ret.put(in.readObject(), in.readObject());
+				return ret;
+			}
+		});
+		register(HashSet.class, new Builder<HashSet>(){
+			@Override
+			public Class<HashSet> getType(){ return HashSet.class; }
+			@Override
+			public void writeTo(HashSet obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUES);
+					out.writeUInt(obj.size());
+					for( Object item : obj )
+						out.writeObject(item);
+				}
+			}
+			@Override
+			public HashSet parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUES )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + ".");
+
+				int len = in.readUInt();
+				HashSet ret = new HashSet(len);
+				for(int i=0;i<len;i++)
+					ret.add(in.readObject());
+				return ret;
+			}
+		});
+
+		register(Date.class, new Builder<Date>(){
+			@Override
+			public Class<Date> getType(){ return Date.class; }
+			@Override
+			public void writeTo(Date obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeLong(obj.getTime());
+				}
+			}
+			@Override
+			public Date parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return new Date(in.readLong());
+			}
+		});
+
+		// java.sql
+		register(java.sql.Date.class, new Builder<java.sql.Date>(){
+			@Override
+			public Class<java.sql.Date> getType(){ return java.sql.Date.class; }
+			@Override
+			public void writeTo(java.sql.Date obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeLong(obj.getTime());
+				}
+			}
+			@Override
+			public java.sql.Date parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return new java.sql.Date(in.readLong());
+			}
+		});
+		register(java.sql.Timestamp.class, new Builder<java.sql.Timestamp>(){
+			@Override
+			public Class<java.sql.Timestamp> getType(){ return java.sql.Timestamp.class; }
+			@Override
+			public void writeTo(java.sql.Timestamp obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeLong(obj.getTime());
+				}
+			}
+			@Override
+			public java.sql.Timestamp parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return new java.sql.Timestamp(in.readLong());
+			}
+		});
+		register(java.sql.Time.class, new Builder<java.sql.Time>(){
+			@Override
+			public Class<java.sql.Time> getType(){ return java.sql.Time.class; }
+			@Override
+			public void writeTo(java.sql.Time obj, GenericObjectOutput out) throws IOException
+			{
+				if( obj == null )
+				{
+					out.write0(OBJECT_NULL);
+				}
+				else
+				{
+					out.write0(OBJECT_VALUE);
+					out.writeLong(obj.getTime());
+				}
+			}
+			@Override
+			public java.sql.Time parseFrom(GenericObjectInput in) throws IOException
+			{
+				byte b = in.read0();
+				if( b == OBJECT_NULL )
+					return null;
+				if( b != OBJECT_VALUE )
+					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+				return new java.sql.Time(in.readLong());
+			}
+		});
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.java
new file mode 100644
index 0000000..3b8a240
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.dubbo;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.dubbo.common.serialize.Serialization;

+

+/**

+ * @author ding.lid

+ */

+public class DubboSerialization implements Serialization {

+

+    public byte getContentTypeId() {

+        return 1;

+    }

+

+    public String getContentType() {

+        return "x-application/dubbo";

+    }

+

+    public ObjectOutput serialize(URL url, OutputStream out) throws IOException {

+        return new GenericObjectOutput(out);

+    }

+

+    public ObjectInput deserialize(URL url, InputStream is) throws IOException {

+        return new GenericObjectInput(is);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataFlags.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataFlags.java
new file mode 100644
index 0000000..efab960
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataFlags.java
@@ -0,0 +1,68 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.dubbo;
+
+/**
+ * Constants.
+ * 
+ * @author qian.lei
+ */
+
+public interface GenericDataFlags
+{
+	// prefix three bits
+	byte VARINT = 0, OBJECT = (byte)0x80;
+
+	// varint tag
+	byte VARINT8 = VARINT, VARINT16 = VARINT | 1, VARINT24 = VARINT | 2, VARINT32 = VARINT | 3;
+
+	byte VARINT40 = VARINT | 4, VARINT48 = VARINT | 5, VARINT56 = VARINT | 6, VARINT64 = VARINT | 7;
+
+	// varint contants
+	byte VARINT_NF = VARINT | 10, VARINT_NE = VARINT | 11, VARINT_ND = VARINT | 12;
+
+	byte VARINT_NC = VARINT | 13, VARINT_NB = VARINT | 14, VARINT_NA = VARINT | 15, VARINT_N9 = VARINT | 16;
+
+	byte VARINT_N8 = VARINT | 17, VARINT_N7 = VARINT | 18, VARINT_N6 = VARINT | 19, VARINT_N5 = VARINT | 20;
+
+	byte VARINT_N4 = VARINT | 21, VARINT_N3 = VARINT | 22, VARINT_N2 = VARINT | 23, VARINT_N1 = VARINT | 24;
+
+	byte VARINT_0 = VARINT | 25, VARINT_1 = VARINT | 26, VARINT_2 = VARINT | 27, VARINT_3 = VARINT | 28;
+
+	byte VARINT_4 = VARINT | 29, VARINT_5 = VARINT | 30, VARINT_6 = VARINT | 31, VARINT_7 = VARINT | 32;
+
+	byte VARINT_8 = VARINT | 33, VARINT_9 = VARINT | 34, VARINT_A = VARINT | 35, VARINT_B = VARINT | 36;
+
+	byte VARINT_C = VARINT | 37, VARINT_D = VARINT | 38, VARINT_E = VARINT | 39, VARINT_F = VARINT | 40;
+
+	byte VARINT_10 = VARINT | 41, VARINT_11 = VARINT | 42, VARINT_12 = VARINT | 43, VARINT_13 = VARINT | 44;
+
+	byte VARINT_14 = VARINT | 45, VARINT_15 = VARINT | 46, VARINT_16 = VARINT | 47, VARINT_17 = VARINT | 48;
+
+	byte VARINT_18 = VARINT | 49, VARINT_19 = VARINT | 50, VARINT_1A = VARINT | 51, VARINT_1B = VARINT | 52;
+
+	byte VARINT_1C = VARINT | 53, VARINT_1D = VARINT | 54, VARINT_1E = VARINT | 55, VARINT_1F = VARINT | 56;
+
+	// object tag
+	byte OBJECT_REF = OBJECT | 1, OBJECT_STREAM = OBJECT | 2, OBJECT_BYTES = OBJECT | 3;
+
+	byte OBJECT_VALUE = OBJECT | 4, OBJECT_VALUES = OBJECT | 5, OBJECT_MAP = OBJECT | 6;
+
+	byte OBJECT_DESC = OBJECT | 10, OBJECT_DESC_ID = OBJECT | 11;
+
+	// object constants
+	byte OBJECT_NULL = OBJECT | 20, OBJECT_DUMMY = OBJECT | 21;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataInput.java
new file mode 100644
index 0000000..a9dc954
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataInput.java
@@ -0,0 +1,395 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.dubbo;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UTFDataFormatException;
+
+import com.alibaba.dubbo.common.serialize.DataInput;
+
+/**
+ * Default DataInput impl.
+ * Not thread-safe.
+ * 
+ * @author qian.lei
+ */
+
+public class GenericDataInput implements DataInput, GenericDataFlags
+{
+	private static final String EMPTY_STRING = "";
+
+	private static final byte[] EMPTY_BYTES = {};
+
+	private final InputStream mInput;
+
+	private final byte[] mBuffer;
+
+	private int mRead = 0;
+
+	private int mPosition = 0;
+
+	public GenericDataInput(InputStream is)
+	{
+		this(is, 1024);
+	}
+
+	public GenericDataInput(InputStream is, int buffSize)
+	{
+		mInput = is;
+		mBuffer = new byte[buffSize];
+	}
+
+	public boolean readBool() throws IOException
+	{
+		byte b = read0();
+
+		switch( b )
+		{
+			case VARINT_0: return false;
+			case VARINT_1: return true;
+			default:
+				throw new IOException("Tag error, expect BYTE_TRUE|BYTE_FALSE, but get " + b);
+		}
+	}
+
+	public byte readByte() throws IOException
+	{
+		byte b = read0();
+
+		switch( b )
+		{
+			case VARINT8:
+				return read0();
+			case VARINT_0: return 0; case VARINT_1: return 1; case VARINT_2: return 2; case VARINT_3: return 3;
+			case VARINT_4: return 4; case VARINT_5: return 5; case VARINT_6: return 6; case VARINT_7: return 7;
+			case VARINT_8: return 8; case VARINT_9: return 9; case VARINT_A: return 10; case VARINT_B: return 11;
+			case VARINT_C: return 12; case VARINT_D: return 13; case VARINT_E: return 14; case VARINT_F: return 15;
+			case VARINT_10: return 16; case VARINT_11: return 17; case VARINT_12: return 18; case VARINT_13: return 19;
+			case VARINT_14: return 20; case VARINT_15: return 21; case VARINT_16: return 22; case VARINT_17: return 23;
+			case VARINT_18: return 24; case VARINT_19: return 25; case VARINT_1A: return 26; case VARINT_1B: return 27;
+			case VARINT_1C: return 28; case VARINT_1D: return 29; case VARINT_1E: return 30; case VARINT_1F: return 31;
+			default:
+				throw new IOException("Tag error, expect VARINT, but get " + b);
+		}
+	}
+
+	public short readShort() throws IOException
+	{
+		return (short)readVarint32();
+	}
+
+	public int readInt() throws IOException
+	{
+		return readVarint32();
+	}
+
+	public long readLong() throws IOException
+	{
+		return readVarint64();
+	}
+
+	public float readFloat() throws IOException
+	{
+		return Float.intBitsToFloat(readVarint32());
+	}
+
+	public double readDouble() throws IOException
+	{
+		return Double.longBitsToDouble(readVarint64());
+	}
+
+	public String readUTF() throws IOException
+	{
+		byte b = read0();
+
+		switch( b )
+		{
+			case OBJECT_BYTES:
+				int len = readUInt();
+				StringBuilder sb = new StringBuilder();
+
+				for(int i=0;i<len;i++)
+				{
+					byte b1 = read0();
+					if( (b1 & 0x80) == 0 )
+					{
+						sb.append((char)b1);
+					}
+					else if( (b1 & 0xE0) == 0xC0 )
+					{
+						byte b2 = read0();
+						sb.append((char)(((b1 & 0x1F) << 6) | (b2 & 0x3F)));
+					}
+					else if( (b1 & 0xF0) == 0xE0 )
+					{
+						byte b2 = read0(), b3 = read0();
+						sb.append((char)(((b1 & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F)));
+					}
+					else
+						throw new UTFDataFormatException("Bad utf-8 encoding at " + b1);
+				}
+				return sb.toString();
+			case OBJECT_NULL: return null;
+			case OBJECT_DUMMY: return EMPTY_STRING;
+			default:
+				throw new IOException("Tag error, expect BYTES|BYTES_NULL|BYTES_EMPTY, but get " + b);
+		}
+	}
+
+	public byte[] readBytes() throws IOException
+	{
+		byte b = read0();
+
+		switch( b )
+		{
+			case OBJECT_BYTES: return read0(readUInt());
+			case OBJECT_NULL: return null;
+			case OBJECT_DUMMY: return EMPTY_BYTES;
+			default:
+				throw new IOException("Tag error, expect BYTES|BYTES_NULL|BYTES_EMPTY, but get " + b);
+		}
+	}
+
+	public int readUInt() throws IOException
+	{
+		byte tmp = read0();
+		if( tmp < 0 )
+			return tmp & 0x7f;
+
+		int ret = tmp & 0x7f;
+		if( ( tmp = read0() ) < 0 )
+		{
+			ret |= ( tmp & 0x7f ) << 7;
+		}
+		else
+		{
+			ret |= tmp << 7;
+			if( ( tmp = read0() ) < 0 )
+			{
+				ret |= ( tmp & 0x7f ) << 14;
+			}
+			else
+			{
+				ret |= tmp << 14;
+				if( ( tmp = read0() ) < 0 )
+				{
+			        ret |= ( tmp & 0x7f ) << 21;
+				}
+				else
+				{
+					ret |= tmp << 21;
+					ret |= ( read0() & 0x7f ) << 28;
+				}
+			}
+		}
+		return ret;
+	}
+
+	protected byte read0() throws IOException
+	{
+		if( mPosition == mRead )
+			fillBuffer();
+
+		return mBuffer[mPosition++];
+	}
+
+	protected byte[] read0(int len) throws IOException
+	{
+		int rem = mRead - mPosition;
+		byte[] ret = new byte[len];
+		if( len <= rem )
+		{
+			System.arraycopy(mBuffer, mPosition, ret, 0, len);
+			mPosition += len;
+		}
+		else
+		{
+			System.arraycopy(mBuffer, mPosition, ret, 0, rem);
+			mPosition = mRead;
+
+			len -= rem;
+			int read, pos = rem;
+
+			while( len > 0 )
+			{
+				read = mInput.read(ret, pos, len);
+				if( read == -1 )
+					throw new EOFException();
+				pos += read;
+				len -= read;
+			}
+		}
+		return ret;
+	}
+
+	private int readVarint32() throws IOException
+	{
+		byte b = read0();
+
+		switch( b )
+		{
+			case VARINT8:
+				return read0();
+			case VARINT16:
+			{
+				byte b1 = read0(), b2 = read0();
+				return (short)( ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) );
+			}
+			case VARINT24:
+			{
+				byte b1 = read0(), b2 = read0(), b3 = read0();
+				int ret = ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) | ( ( b3 & 0xff ) << 16 );
+				if( b3 < 0 )
+					return ret | 0xff000000;
+				return ret;
+			}
+			case VARINT32:
+			{
+				byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0();
+				return ( ( b1 & 0xff ) |
+					( ( b2 & 0xff ) << 8 ) |
+					( ( b3 & 0xff ) << 16 ) |
+					( ( b4 & 0xff ) << 24 ) );
+			}
+			case VARINT_NF: return -15; case VARINT_NE: return -14; case VARINT_ND: return -13;
+			case VARINT_NC: return -12; case VARINT_NB: return -11; case VARINT_NA: return -10; case VARINT_N9: return -9;
+			case VARINT_N8: return -8; case VARINT_N7: return -7; case VARINT_N6: return -6; case VARINT_N5: return -5;
+			case VARINT_N4: return -4; case VARINT_N3: return -3; case VARINT_N2: return -2; case VARINT_N1: return -1;
+			case VARINT_0: return 0; case VARINT_1: return 1; case VARINT_2: return 2; case VARINT_3: return 3;
+			case VARINT_4: return 4; case VARINT_5: return 5; case VARINT_6: return 6; case VARINT_7: return 7;
+			case VARINT_8: return 8; case VARINT_9: return 9; case VARINT_A: return 10; case VARINT_B: return 11;
+			case VARINT_C: return 12; case VARINT_D: return 13; case VARINT_E: return 14; case VARINT_F: return 15;
+			case VARINT_10: return 16; case VARINT_11: return 17; case VARINT_12: return 18; case VARINT_13: return 19;
+			case VARINT_14: return 20; case VARINT_15: return 21; case VARINT_16: return 22; case VARINT_17: return 23;
+			case VARINT_18: return 24; case VARINT_19: return 25; case VARINT_1A: return 26; case VARINT_1B: return 27;
+			case VARINT_1C: return 28; case VARINT_1D: return 29; case VARINT_1E: return 30; case VARINT_1F: return 31;
+			default:
+				throw new IOException("Tag error, expect VARINT, but get " + b);
+		}
+	}
+
+	private long readVarint64() throws IOException
+	{
+		byte b = read0();
+
+		switch( b )
+		{
+			case VARINT8:
+				return read0();
+			case VARINT16:
+			{
+				byte b1 = read0(), b2 = read0();
+				return (short)( ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) );
+			}
+			case VARINT24:
+			{
+				byte b1 = read0(), b2 = read0(), b3 = read0();
+				int ret = ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) | ( ( b3 & 0xff ) << 16 );
+				if( b3 < 0 )
+					return ret | 0xff000000;
+				return ret;
+			}
+			case VARINT32:
+			{
+				byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0();
+				return ( ( b1 & 0xff ) |
+					( ( b2 & 0xff ) << 8 ) |
+					( ( b3 & 0xff ) << 16 ) |
+					( ( b4 & 0xff ) << 24 ) );
+			}
+			case VARINT40:
+			{
+				byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0(), b5 = read0();
+				long ret = ( (long)b1 & 0xff ) |
+					( ( (long)b2 & 0xff ) << 8 ) |
+					( ( (long)b3 & 0xff ) << 16 ) |
+					( ( (long)b4 & 0xff ) << 24 ) |
+					( ( (long)b5 & 0xff ) << 32 );
+				if( b5 < 0 )
+					return ret | 0xffffff0000000000l;
+				return ret;
+			}
+			case VARINT48:
+			{
+				byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0(), b5 = read0(), b6 = read0();
+				long ret = ( (long)b1 & 0xff ) |
+					( ( (long)b2 & 0xff ) << 8 ) |
+					( ( (long)b3 & 0xff ) << 16 ) |
+					( ( (long)b4 & 0xff ) << 24 ) |
+					( ( (long)b5 & 0xff ) << 32 ) |
+					( ( (long)b6 & 0xff ) << 40 );
+				if( b6 < 0 )
+					return ret | 0xffff000000000000l;
+				return ret;
+			}
+			case VARINT56:
+			{
+				byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0(), b5 = read0(), b6 = read0(), b7 = read0();
+				long ret = ( (long)b1 & 0xff ) |
+					( ( (long)b2 & 0xff ) << 8 ) |
+					( ( (long)b3 & 0xff ) << 16 ) |
+					( ( (long)b4 & 0xff ) << 24 ) |
+					( ( (long)b5 & 0xff ) << 32 ) |
+					( ( (long)b6 & 0xff ) << 40 ) |
+					( ( (long)b7 & 0xff ) << 48 );
+				if( b7 < 0 )
+					return ret | 0xff00000000000000l;
+				return ret;
+			}
+			case VARINT64:
+			{
+				byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0();
+				byte b5 = read0(), b6 = read0(), b7 = read0(), b8 = read0();
+				return ( ( (long)b1 & 0xff ) |
+					( ( (long)b2 & 0xff ) << 8 ) |
+					( ( (long)b3 & 0xff ) << 16 ) |
+					( ( (long)b4 & 0xff ) << 24 ) |
+					( ( (long)b5 & 0xff ) << 32 ) |
+					( ( (long)b6 & 0xff ) << 40 ) |
+					( ( (long)b7 & 0xff ) << 48 ) |
+					( ( (long)b8 & 0xff ) << 56 ) );
+			}
+			case VARINT_NF: return -15; case VARINT_NE: return -14; case VARINT_ND: return -13;
+			case VARINT_NC: return -12; case VARINT_NB: return -11; case VARINT_NA: return -10; case VARINT_N9: return -9;
+			case VARINT_N8: return -8; case VARINT_N7: return -7; case VARINT_N6: return -6; case VARINT_N5: return -5;
+			case VARINT_N4: return -4; case VARINT_N3: return -3; case VARINT_N2: return -2; case VARINT_N1: return -1;
+			case VARINT_0: return 0; case VARINT_1: return 1; case VARINT_2: return 2; case VARINT_3: return 3;
+			case VARINT_4: return 4; case VARINT_5: return 5; case VARINT_6: return 6; case VARINT_7: return 7;
+			case VARINT_8: return 8; case VARINT_9: return 9; case VARINT_A: return 10; case VARINT_B: return 11;
+			case VARINT_C: return 12; case VARINT_D: return 13; case VARINT_E: return 14; case VARINT_F: return 15;
+			case VARINT_10: return 16; case VARINT_11: return 17; case VARINT_12: return 18; case VARINT_13: return 19;
+			case VARINT_14: return 20; case VARINT_15: return 21; case VARINT_16: return 22; case VARINT_17: return 23;
+			case VARINT_18: return 24; case VARINT_19: return 25; case VARINT_1A: return 26; case VARINT_1B: return 27;
+			case VARINT_1C: return 28; case VARINT_1D: return 29; case VARINT_1E: return 30; case VARINT_1F: return 31;
+			default:
+				throw new IOException("Tag error, expect VARINT, but get " + b);
+		}
+	}
+
+	private void fillBuffer() throws IOException
+	{
+		mPosition = 0;
+		mRead = mInput.read(mBuffer);
+
+		if( mRead == -1 )
+		{
+			mRead = 0;
+			throw new EOFException();
+		}
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataOutput.java
new file mode 100644
index 0000000..b50ca1f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataOutput.java
@@ -0,0 +1,344 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.serialize.DataOutput;
+
+/**
+ * Default data output impl.
+ * Not thread-safe.
+ * 
+ * @author qian.lei
+ */
+
+public class GenericDataOutput implements DataOutput, GenericDataFlags
+{
+	private static final int CHAR_BUF_SIZE = 256;
+
+	private final byte[] mBuffer, mTemp = new byte[9];
+
+	private final char[] mCharBuf = new char[CHAR_BUF_SIZE];
+
+	private final OutputStream mOutput;
+
+	private final int mLimit;
+
+	private int mPosition = 0;
+
+	public GenericDataOutput(OutputStream out)
+	{
+		this(out, 1024);
+	}
+
+	public GenericDataOutput(OutputStream out, int buffSize)
+	{
+		mOutput = out;
+		mLimit = buffSize;
+		mBuffer = new byte[buffSize];
+	}
+
+	public void writeBool(boolean v) throws IOException
+	{
+		write0( v ? VARINT_1 : VARINT_0 );
+	}
+
+	public void writeByte(byte v) throws IOException
+	{
+		switch( v )
+		{
+			case 0: write0(VARINT_0); break; case 1: write0(VARINT_1); break; case 2: write0(VARINT_2); break; case 3: write0(VARINT_3); break;
+			case 4: write0(VARINT_4); break; case 5: write0(VARINT_5); break; case 6: write0(VARINT_6); break; case 7: write0(VARINT_7); break;
+			case 8: write0(VARINT_8); break; case 9: write0(VARINT_9); break; case 10: write0(VARINT_A); break; case 11: write0(VARINT_B); break;
+			case 12: write0(VARINT_C); break; case 13: write0(VARINT_D); break; case 14: write0(VARINT_E); break; case 15: write0(VARINT_F); break;
+			case 16: write0(VARINT_10); break; case 17: write0(VARINT_11); break; case 18: write0(VARINT_12); break; case 19: write0(VARINT_13); break;
+			case 20: write0(VARINT_14); break; case 21: write0(VARINT_15); break; case 22: write0(VARINT_16); break; case 23: write0(VARINT_17); break;
+			case 24: write0(VARINT_18); break; case 25: write0(VARINT_19); break; case 26: write0(VARINT_1A); break; case 27: write0(VARINT_1B); break;
+			case 28: write0(VARINT_1C); break; case 29: write0(VARINT_1D); break; case 30: write0(VARINT_1E); break; case 31: write0(VARINT_1F); break;
+			default:
+				write0(VARINT8);
+				write0(v);
+		}
+	}
+
+	public void writeShort(short v) throws IOException
+	{
+		writeVarint32(v);
+	}
+
+	public void writeInt(int v) throws IOException
+	{
+		writeVarint32(v);
+	}
+
+	public void writeLong(long v) throws IOException
+	{
+		writeVarint64(v);
+	}
+
+	public void writeFloat(float v) throws IOException
+	{
+		writeVarint32(Float.floatToRawIntBits(v));
+	}
+
+	public void writeDouble(double v) throws IOException
+	{
+		writeVarint64(Double.doubleToRawLongBits(v));
+	}
+
+	public void writeUTF(String v) throws IOException
+	{
+		if( v == null )
+		{
+			write0(OBJECT_NULL);
+		}
+		else
+		{
+			int len = v.length();
+			if( len == 0 )
+			{
+				write0(OBJECT_DUMMY);
+			}
+			else
+			{
+				write0(OBJECT_BYTES);
+				writeUInt(len);
+
+				int off = 0, limit = mLimit - 3, size;
+				char[] buf = mCharBuf;
+				do
+				{
+					size = Math.min(len-off, CHAR_BUF_SIZE);
+					v.getChars(off, off+size, buf, 0);
+
+					for(int i=0;i<size;i++)
+					{
+						char c = buf[i];
+						if( mPosition > limit )
+						{
+							if( c < 0x80 )
+							{
+								write0((byte)c);
+							}
+							else if( c < 0x800 )
+							{
+								write0((byte)(0xC0 | ((c >> 6) & 0x1F)));
+								write0((byte)(0x80 | (c & 0x3F)));
+							}
+							else
+							{
+								write0((byte)(0xE0 | ((c >> 12) & 0x0F)));
+								write0((byte)(0x80 | ((c >> 6) & 0x3F)));
+								write0((byte)(0x80 | (c & 0x3F)));
+							}
+						}
+						else
+						{
+							if( c < 0x80 )
+							{
+								mBuffer[mPosition++] = (byte)c;
+							}
+							else if( c < 0x800 )
+							{
+								mBuffer[mPosition++] = (byte)(0xC0 | ((c >> 6) & 0x1F));
+								mBuffer[mPosition++] = (byte)(0x80 | (c & 0x3F));
+							}
+							else
+							{
+								mBuffer[mPosition++] = (byte)(0xE0 | ((c >> 12) & 0x0F));
+								mBuffer[mPosition++] = (byte)(0x80 | ((c >> 6) & 0x3F));
+								mBuffer[mPosition++] = (byte)(0x80 | (c & 0x3F));
+							}
+						}
+					}
+					off += size;
+				}
+				while( off < len );
+			}
+		}
+	}
+
+	public void writeBytes(byte[] b) throws IOException
+	{
+		if( b == null )
+			write0(OBJECT_NULL);
+		else
+			writeBytes(b, 0, b.length);
+	}
+
+	public void writeBytes(byte[] b, int off, int len) throws IOException
+	{
+		if( len == 0 )
+		{
+			write0(OBJECT_DUMMY);
+		}
+		else
+		{
+			write0(OBJECT_BYTES);
+			writeUInt(len);
+			write0(b, off, len);
+		}
+	}
+
+	public void flushBuffer() throws IOException
+	{
+		if( mPosition > 0 )
+		{
+			mOutput.write(mBuffer, 0, mPosition);
+			mPosition = 0;
+		}
+	}
+
+	public void writeUInt(int v) throws IOException
+	{
+		byte tmp;
+		while( true )
+		{
+			tmp = (byte)( v & 0x7f );
+			if( ( v >>>= 7 ) == 0 )
+			{
+				write0( (byte)( tmp | 0x80 ) );
+				return;
+			}
+			else
+			{
+				write0(tmp);
+			}
+		}
+	}
+
+	protected void write0(byte b) throws IOException
+	{
+		if( mPosition == mLimit )
+			flushBuffer();
+
+		mBuffer[mPosition++] = b;
+	}
+
+	protected void write0(byte[] b, int off, int len) throws IOException
+	{
+		int rem = mLimit - mPosition;
+		if( rem > len )
+		{
+			System.arraycopy(b, off, mBuffer, mPosition, len);
+			mPosition += len;
+		}
+		else
+		{
+			System.arraycopy(b, off, mBuffer, mPosition, rem);
+			mPosition = mLimit;
+			flushBuffer();
+
+			off += rem;
+			len -= rem;
+
+			if( mLimit > len )
+			{
+				System.arraycopy(b, off, mBuffer, 0, len);
+				mPosition = len;
+			}
+			else
+			{
+				mOutput.write(b, off, len);
+			}
+		}
+	}
+
+	private void writeVarint32(int v) throws IOException
+	{
+		switch( v )
+		{
+			case -15: write0(VARINT_NF); break; case -14: write0(VARINT_NE); break; case -13: write0(VARINT_ND); break;
+			case -12: write0(VARINT_NC); break; case -11: write0(VARINT_NB); break; case -10: write0(VARINT_NA); break; case -9: write0(VARINT_N9); break;
+			case -8: write0(VARINT_N8); break; case -7: write0(VARINT_N7); break; case -6: write0(VARINT_N6); break; case -5: write0(VARINT_N5); break;
+			case -4: write0(VARINT_N4); break; case -3: write0(VARINT_N3); break; case -2: write0(VARINT_N2); break; case -1: write0(VARINT_N1); break;
+			case 0: write0(VARINT_0); break; case 1: write0(VARINT_1); break; case 2: write0(VARINT_2); break; case 3: write0(VARINT_3); break;
+			case 4: write0(VARINT_4); break; case 5: write0(VARINT_5); break; case 6: write0(VARINT_6); break; case 7: write0(VARINT_7); break;
+			case 8: write0(VARINT_8); break; case 9: write0(VARINT_9); break; case 10: write0(VARINT_A); break; case 11: write0(VARINT_B); break;
+			case 12: write0(VARINT_C); break; case 13: write0(VARINT_D); break; case 14: write0(VARINT_E); break; case 15: write0(VARINT_F); break;
+			case 16: write0(VARINT_10); break; case 17: write0(VARINT_11); break; case 18: write0(VARINT_12); break; case 19: write0(VARINT_13); break;
+			case 20: write0(VARINT_14); break; case 21: write0(VARINT_15); break; case 22: write0(VARINT_16); break; case 23: write0(VARINT_17); break;
+			case 24: write0(VARINT_18); break; case 25: write0(VARINT_19); break; case 26: write0(VARINT_1A); break; case 27: write0(VARINT_1B); break;
+			case 28: write0(VARINT_1C); break; case 29: write0(VARINT_1D); break; case 30: write0(VARINT_1E); break; case 31: write0(VARINT_1F); break;
+			default:
+				int t = v, ix = 0;
+				byte[] b = mTemp;
+
+				while( true )
+				{
+					b[++ix] = (byte)( v & 0xff );
+					if( ( v >>>= 8 ) == 0 )
+						break;
+				}
+
+				if( t > 0 )
+				{
+					// [ 0a e2 => 0a e2 00 ] [ 92 => 92 00 ]
+					if( b[ix] < 0 )
+						b[++ix] = 0;
+				}
+				else
+				{
+					// [ 01 ff ff ff => 01 ff ] [ e0 ff ff ff => e0 ]
+					while( b[ix] == (byte)0xff && b[ix-1] < 0 )
+						ix--;
+				}
+
+				b[0] = (byte)( VARINT + ix - 1 );
+				write0(b, 0, ix+1);
+		}
+	}
+
+	private void writeVarint64(long v) throws IOException
+	{
+		int i = (int)v;
+		if( v == i )
+		{
+			writeVarint32(i);
+		}
+		else
+		{
+			long t = v;
+			int ix = 0;
+			byte[] b = mTemp;
+
+			while( true )
+			{
+				b[++ix] = (byte)( v & 0xff );
+				if( ( v >>>= 8 ) == 0 )
+					break;
+			}
+
+			if( t > 0 )
+			{
+				// [ 0a e2 => 0a e2 00 ] [ 92 => 92 00 ]
+				if( b[ix] < 0 )
+					b[++ix] = 0;
+			}
+			else
+			{
+				// [ 01 ff ff ff => 01 ff ] [ e0 ff ff ff => e0 ]
+				while( b[ix] == (byte)0xff && b[ix-1] < 0 )
+					ix--;
+			}
+
+			b[0] = (byte)( VARINT + ix - 1 );
+			write0(b, 0, ix+1);
+		}
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectInput.java
new file mode 100644
index 0000000..b8b220a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectInput.java
@@ -0,0 +1,243 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;

+import java.io.InputStream;

+import java.lang.reflect.Type;

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.io.ClassDescriptorMapper;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+
+/**
+ * Generic Object Input.
+ * 
+ * @author qian.lei
+ */
+public class GenericObjectInput extends GenericDataInput implements ObjectInput
+{
+	private static Object SKIPPED_OBJECT = new Object();
+
+	private ClassDescriptorMapper mMapper;
+
+	private List<Object> mRefs = new ArrayList<Object>();
+
+	public GenericObjectInput(InputStream is)
+	{
+		this(is, Builder.DEFAULT_CLASS_DESCRIPTOR_MAPPER);
+	}
+
+	public GenericObjectInput(InputStream is, ClassDescriptorMapper mapper)
+	{
+		super(is);
+		mMapper = mapper;
+	}
+
+	public GenericObjectInput(InputStream is, int buffSize)
+	{
+		this(is, buffSize, Builder.DEFAULT_CLASS_DESCRIPTOR_MAPPER);
+	}
+
+	public GenericObjectInput(InputStream is, int buffSize, ClassDescriptorMapper mapper)
+	{
+		super(is, buffSize);
+		mMapper = mapper;
+	}
+
+	public Object readObject() throws IOException
+	{
+		String desc;
+		byte b = read0();
+
+		switch( b )
+		{
+			case OBJECT_NULL:
+				return null;
+			case OBJECT_DUMMY:
+				return new Object();
+			case OBJECT_DESC:
+			{
+				desc = readUTF();
+				break;
+			}
+			case OBJECT_DESC_ID:
+			{
+				int index = readUInt();
+				desc = mMapper.getDescriptor(index);
+				if( desc == null )
+					throw new IOException("Can not find desc id: " + index );
+				break;
+			}
+			default:
+				throw new IOException("Flag error, expect OBJECT_NULL|OBJECT_DUMMY|OBJECT_DESC|OBJECT_DESC_ID, get " + b);
+		}
+		try
+		{
+			Class<?> c = ReflectUtils.desc2class(desc);
+			return Builder.register(c).parseFrom(this);
+		}
+		catch(ClassNotFoundException e)
+		{
+			throw new IOException("Read object failed, class not found. " + StringUtils.toString(e));
+		}
+	}

+	
+	@SuppressWarnings("unchecked")
+	public <T> T readObject(Class<T> cls) throws IOException,ClassNotFoundException
+	{
+		return (T)readObject();
+	}
+

+    @SuppressWarnings("unchecked")

+    public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException

+    {

+        return (T)readObject();

+    }

+
+	public void addRef(Object obj)
+	{
+		mRefs.add(obj);
+	}
+
+	public Object getRef(int index) throws IOException
+	{
+		if( index < 0 || index >= mRefs.size() )
+			return null;
+
+		Object ret = mRefs.get(index);
+		if( ret == SKIPPED_OBJECT )
+			throw new IOException("Ref skipped-object.");
+		return ret;
+	}
+
+	public void skipAny() throws IOException
+	{
+		byte b = read0();
+		switch( b )
+		{
+			case VARINT_NF: case VARINT_NE: case VARINT_ND: case VARINT_NC: case VARINT_NB: case VARINT_NA: case VARINT_N9:
+			case VARINT_N8: case VARINT_N7: case VARINT_N6: case VARINT_N5: case VARINT_N4: case VARINT_N3: case VARINT_N2: case VARINT_N1:
+			case VARINT_0: case VARINT_1: case VARINT_2: case VARINT_3: case VARINT_4: case VARINT_5: case VARINT_6: case VARINT_7:
+			case VARINT_8: case VARINT_9: case VARINT_A: case VARINT_B: case VARINT_C: case VARINT_D: case VARINT_E: case VARINT_F:
+			case VARINT_10: case VARINT_11: case VARINT_12: case VARINT_13: case VARINT_14: case VARINT_15: case VARINT_16: case VARINT_17:
+			case VARINT_18: case VARINT_19: case VARINT_1A: case VARINT_1B: case VARINT_1C: case VARINT_1D: case VARINT_1E: case VARINT_1F:
+			case OBJECT_NULL: case OBJECT_DUMMY:
+				break;
+			case VARINT8:
+			{
+				read0();
+				break;
+			}
+			case VARINT16:
+			{
+				read0(); read0();
+				break;
+			}
+			case VARINT24:
+			{
+				read0(); read0(); read0();
+				break;
+			}
+			case VARINT32:
+			{
+				read0(); read0(); read0(); read0();
+				break;
+			}
+			case VARINT40:
+			{
+				read0(); read0(); read0(); read0(); read0();
+				break;
+			}
+			case VARINT48:
+			{
+				read0(); read0(); read0(); read0(); read0(); read0();
+				break;
+			}
+			case VARINT56:
+			{
+				read0(); read0(); read0(); read0(); read0(); read0(); read0();
+				break;
+			}
+			case VARINT64:
+			{
+				read0(); read0(); read0(); read0(); read0(); read0(); read0(); read0();
+				break;
+			}
+			case OBJECT:
+			{
+				addRef(SKIPPED_OBJECT);
+				int len = readUInt();
+				for(int i=0;i<len;i++)
+					skipAny();
+				break;
+			}
+			case OBJECT_REF:
+			{
+				readUInt();
+				break;
+			}
+			case OBJECT_STREAM: case OBJECT_BYTES:
+			{
+				read0(readUInt());
+				break;
+			}
+			case OBJECT_VALUE:
+			{
+				skipAny();
+				break;
+			}
+			case OBJECT_VALUES:
+			{
+				int len = readUInt();
+				for(int i=0;i<len;i++)
+					skipAny();
+				break;
+			}
+			case OBJECT_MAP:
+			{
+				int len = readUInt();
+				for(int i=0;i<len;i++)
+				{
+					skipAny(); // skip key
+					skipAny(); // skip value
+				}
+				break;
+			}
+			case OBJECT_DESC:
+			{
+				readUTF();
+				int len = readUInt();
+				for(int i=0;i<len;i++)
+					skipAny();
+				break;
+			}
+			case OBJECT_DESC_ID:
+			{
+				readUInt();
+				int len = readUInt();
+				for(int i=0;i<len;i++)
+					skipAny();
+				break;
+			}
+			default:
+				throw new IOException("Flag error, get " + b);
+		}
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectOutput.java
new file mode 100644
index 0000000..e2f8752
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectOutput.java
@@ -0,0 +1,115 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.io.ClassDescriptorMapper;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+
+/**
+ * Generic Object Output.
+ * 
+ * @author qian.lei
+ */
+
+public class GenericObjectOutput extends GenericDataOutput implements ObjectOutput
+{
+	private ClassDescriptorMapper mMapper;
+
+	private Map<Object, Integer> mRefs = new ConcurrentHashMap<Object, Integer>();
+	
+	private final boolean isAllowNonSerializable;
+
+	public GenericObjectOutput(OutputStream out)
+	{
+		this(out, Builder.DEFAULT_CLASS_DESCRIPTOR_MAPPER);
+	}
+
+	public GenericObjectOutput(OutputStream out, ClassDescriptorMapper mapper)
+	{
+		super(out);
+		mMapper = mapper;
+		isAllowNonSerializable = false;
+	}
+
+	public GenericObjectOutput(OutputStream out, int buffSize)
+	{
+		this(out, buffSize, Builder.DEFAULT_CLASS_DESCRIPTOR_MAPPER, false);
+	}
+
+	public GenericObjectOutput(OutputStream out, int buffSize, ClassDescriptorMapper mapper)
+	{
+		this(out, buffSize, mapper, false);
+	}
+	
+	public GenericObjectOutput(OutputStream out, int buffSize, ClassDescriptorMapper mapper, boolean isAllowNonSerializable)
+	{
+	    super(out, buffSize);
+	    mMapper = mapper;
+	    this.isAllowNonSerializable = isAllowNonSerializable;
+	}
+
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public void writeObject(Object obj) throws IOException
+	{
+		if( obj == null )
+		{
+			write0(OBJECT_NULL);
+			return;
+		}
+		
+		Class<?> c = obj.getClass();
+		if( c == Object.class )
+		{
+			write0(OBJECT_DUMMY);
+		}
+		else
+		{
+			String desc = ReflectUtils.getDesc(c);
+			int index = mMapper.getDescriptorIndex(desc);
+			if( index < 0 )
+			{
+				write0(OBJECT_DESC);
+				writeUTF(desc);
+			}
+			else
+			{
+				write0(OBJECT_DESC_ID);
+				writeUInt(index);
+			}
+			Builder b = Builder.register(c, isAllowNonSerializable);
+			b.writeTo(obj, this);
+		}
+	}
+
+	public void addRef(Object obj)
+	{
+		mRefs.put(obj, mRefs.size());
+	}
+
+	public int getRef(Object obj)
+	{
+		Integer ref = mRefs.get(obj);
+		if( ref == null )
+			return -1;
+		return ref.intValue();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectInput.java
new file mode 100644
index 0000000..c3db300
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectInput.java
@@ -0,0 +1,102 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.hessian;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Type;

+
+import com.alibaba.com.caucho.hessian.io.Hessian2Input;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+
+/**
+ * Hessian2 Object input.
+ * 
+ * @author qian.lei
+ */
+
+public class Hessian2ObjectInput implements ObjectInput
+{
+	private final Hessian2Input mH2i;
+
+	public Hessian2ObjectInput(InputStream is)
+	{
+		mH2i = new Hessian2Input(is);
+		mH2i.setSerializerFactory(Hessian2SerializerFactory.SERIALIZER_FACTORY);
+	}
+
+	public boolean readBool() throws IOException
+	{
+		return mH2i.readBoolean();
+	}
+
+	public byte readByte() throws IOException
+	{
+		return (byte)mH2i.readInt();
+	}
+
+	public short readShort() throws IOException
+	{
+		return (short)mH2i.readInt();
+	}
+
+	public int readInt() throws IOException
+	{
+		return mH2i.readInt();
+	}
+
+	public long readLong() throws IOException
+	{
+		return mH2i.readLong();
+	}
+
+	public float readFloat() throws IOException
+	{
+		return (float)mH2i.readDouble();
+	}
+
+	public double readDouble() throws IOException
+	{
+		return mH2i.readDouble();
+	}
+
+	public byte[] readBytes() throws IOException
+	{
+		return mH2i.readBytes();
+	}
+
+	public String readUTF() throws IOException
+	{
+		return mH2i.readString();
+	}
+
+	public Object readObject() throws IOException
+	{
+		return mH2i.readObject();
+	}
+
+	@SuppressWarnings("unchecked")
+	public <T> T readObject(Class<T> cls) throws IOException,
+			ClassNotFoundException {
+		return (T) mH2i.readObject(cls);
+	}

+

+    public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException

+    {

+        return readObject(cls);

+    }

+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectOutput.java
new file mode 100644
index 0000000..1f3c57d
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectOutput.java
@@ -0,0 +1,99 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.hessian;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.alibaba.com.caucho.hessian.io.Hessian2Output;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * Hessian2 Object output.
+ * 
+ * @author qian.lei
+ */
+
+public class Hessian2ObjectOutput implements ObjectOutput
+{
+	private final Hessian2Output mH2o;
+
+	public Hessian2ObjectOutput(OutputStream os)
+	{
+		mH2o = new Hessian2Output(os);
+		mH2o.setSerializerFactory(Hessian2SerializerFactory.SERIALIZER_FACTORY);
+	}
+
+	public void writeBool(boolean v) throws IOException
+	{
+		mH2o.writeBoolean(v);
+	}
+
+	public void writeByte(byte v) throws IOException
+	{
+		mH2o.writeInt(v);
+	}
+
+	public void writeShort(short v) throws IOException
+	{
+		mH2o.writeInt(v);
+	}
+
+	public void writeInt(int v) throws IOException
+	{
+		mH2o.writeInt(v);
+	}
+
+	public void writeLong(long v) throws IOException
+	{
+		mH2o.writeLong(v);
+	}
+
+	public void writeFloat(float v) throws IOException
+	{
+		mH2o.writeDouble(v);
+	}
+
+	public void writeDouble(double v) throws IOException
+	{
+		mH2o.writeDouble(v);
+	}
+
+	public void writeBytes(byte[] b) throws IOException
+	{
+		mH2o.writeBytes(b);
+	}
+
+	public void writeBytes(byte[] b, int off, int len) throws IOException
+	{
+		mH2o.writeBytes(b, off, len);
+	}
+
+	public void writeUTF(String v) throws IOException
+	{
+		mH2o.writeString(v);
+	}
+
+	public void writeObject(Object obj) throws IOException
+	{
+		mH2o.writeObject(obj);
+	}
+
+	public void flushBuffer() throws IOException
+	{
+		mH2o.flushBuffer();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.java
new file mode 100644
index 0000000..fa61b1b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.hessian;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.dubbo.common.serialize.Serialization;

+

+/**

+ * @author ding.lid

+ */

+public class Hessian2Serialization implements Serialization {

+    

+    public static final byte ID = 2;

+

+    public byte getContentTypeId() {

+        return ID;

+    }

+

+    public String getContentType() {

+        return "x-application/hessian2";

+    }

+

+    public ObjectOutput serialize(URL url, OutputStream out) throws IOException {

+        return new Hessian2ObjectOutput(out);

+    }

+

+    public ObjectInput deserialize(URL url, InputStream is) throws IOException {

+        return new Hessian2ObjectInput(is);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2SerializerFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2SerializerFactory.java
new file mode 100644
index 0000000..5289be0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2SerializerFactory.java
@@ -0,0 +1,32 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.hessian;

+

+import com.alibaba.com.caucho.hessian.io.SerializerFactory;

+

+public class Hessian2SerializerFactory extends SerializerFactory {

+

+	public static final SerializerFactory SERIALIZER_FACTORY = new Hessian2SerializerFactory();

+

+	private Hessian2SerializerFactory() {

+	}

+

+	@Override

+	public ClassLoader getClassLoader() {

+		return Thread.currentThread().getContextClassLoader();

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.java
new file mode 100644
index 0000000..7b6fd26
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.java;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.dubbo.common.serialize.Serialization;

+

+/**

+ * @author ding.lid

+ */

+public class CompactedJavaSerialization implements Serialization {

+

+    public byte getContentTypeId() {

+        return 4;

+    }

+

+    public String getContentType() {

+        return "x-application/compactedjava";

+    }

+

+    public ObjectOutput serialize(URL url, OutputStream out) throws IOException {

+        return new JavaObjectOutput(out, true);

+    }

+

+    public ObjectInput deserialize(URL url, InputStream is) throws IOException {

+        return new JavaObjectInput(is, true);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectInputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectInputStream.java
new file mode 100644
index 0000000..34068f6
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectInputStream.java
@@ -0,0 +1,70 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.java;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.io.StreamCorruptedException;
+
+import com.alibaba.dubbo.common.utils.ClassHelper;
+
+/**
+ * Compacted java object input stream.
+ * 
+ * @author qianlei
+ */
+
+public class CompactedObjectInputStream extends ObjectInputStream
+{
+	private ClassLoader mClassLoader;
+
+	public CompactedObjectInputStream(InputStream in) throws IOException
+	{
+		this(in, Thread.currentThread().getContextClassLoader());
+	}
+
+	public CompactedObjectInputStream(InputStream in, ClassLoader cl) throws IOException
+	{
+		super(in);
+		mClassLoader = cl == null ? ClassHelper.getClassLoader() : cl;
+	}
+
+	@Override
+	protected ObjectStreamClass readClassDescriptor() throws IOException,ClassNotFoundException
+	{
+		int type = read();
+		if( type < 0 )
+			throw new EOFException();
+		switch( type )
+		{
+			case 0:
+				return super.readClassDescriptor();
+			case 1:
+				Class<?> clazz = loadClass(readUTF());
+				return ObjectStreamClass.lookup(clazz);
+			default:
+				throw new StreamCorruptedException("Unexpected class descriptor type: " + type);
+		}
+	}
+
+	private Class<?> loadClass(String className) throws ClassNotFoundException
+	{
+		return mClassLoader.loadClass(className);
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectOutputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectOutputStream.java
new file mode 100644
index 0000000..716cc21
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectOutputStream.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
+
+/**
+ * Compacted java object output stream.
+ * 
+ * @author qianlei
+ */
+
+public class CompactedObjectOutputStream extends ObjectOutputStream
+{
+	public CompactedObjectOutputStream(OutputStream out) throws IOException
+	{
+		super(out);
+	}
+
+	@Override
+	protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException
+	{
+		Class<?> clazz = desc.forClass();
+		if( clazz.isPrimitive() || clazz.isArray() )
+		{
+			write(0);
+			super.writeClassDescriptor(desc);
+		}
+		else
+		{
+			write(1);
+			writeUTF(desc.getName());
+		}
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectInput.java
new file mode 100644
index 0000000..e79d3b1
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectInput.java
@@ -0,0 +1,127 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.lang.reflect.Type;

+
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+
+/**
+ * Java Object input.
+ * 
+ * @author qian.lei
+ */
+
+public class JavaObjectInput implements ObjectInput
+{
+	public final static int MAX_BYTE_ARRAY_LENGTH = 8 * 1024 * 1024;
+
+	private final ObjectInputStream mIn;
+
+	public JavaObjectInput(InputStream is) throws IOException
+	{
+		mIn = new ObjectInputStream(is);
+	}
+
+	public JavaObjectInput(InputStream is, boolean compacted) throws IOException
+	{
+		mIn = compacted ? new CompactedObjectInputStream(is) : new ObjectInputStream(is);
+	}
+
+	public boolean readBool() throws IOException
+	{
+		return mIn.readBoolean();
+	}
+
+	public byte readByte() throws IOException
+	{
+		return mIn.readByte();
+	}
+
+	public short readShort() throws IOException
+	{
+		return mIn.readShort();
+	}
+
+	public int readInt() throws IOException
+	{
+		return mIn.readInt();
+	}
+
+	public long readLong() throws IOException
+	{
+		return mIn.readLong();
+	}
+
+	public float readFloat() throws IOException
+	{
+		return mIn.readFloat();
+	}
+
+	public double readDouble() throws IOException
+	{
+		return mIn.readDouble();
+	}
+
+	public byte[] readBytes() throws IOException
+	{
+		int len = mIn.readInt();
+		if( len < 0 )
+			return null;
+		if( len == 0 )
+			return new byte[0];
+		if( len > MAX_BYTE_ARRAY_LENGTH )
+			throw new IOException("Byte array length too large. " + len);
+
+		byte[] b = new byte[len];
+		mIn.readFully(b);
+		return b;
+	}
+
+	public String readUTF() throws IOException
+	{
+		int len = mIn.readInt();
+		if( len < 0 )
+			return null;
+
+		return mIn.readUTF();
+	}
+
+	public Object readObject() throws IOException, ClassNotFoundException
+	{
+		byte b = mIn.readByte();
+		if( b == 0 )
+			return null;
+
+		return mIn.readObject();
+	}
+
+	@SuppressWarnings("unchecked")
+	public <T> T readObject(Class<T> cls) throws IOException,
+			ClassNotFoundException {
+		return (T) readObject();
+	}
+

+	@SuppressWarnings("unchecked")

+    public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException

+    {

+        return (T) readObject();

+    }

+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectOutput.java
new file mode 100644
index 0000000..f12e031
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectOutput.java
@@ -0,0 +1,123 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * Java Object output.
+ * 
+ * @author qian.lei
+ */
+
+public class JavaObjectOutput implements ObjectOutput
+{
+	private final ObjectOutputStream mOut;
+
+	public JavaObjectOutput(OutputStream os) throws IOException
+	{
+		mOut = new ObjectOutputStream(os);
+	}
+
+	public JavaObjectOutput(OutputStream os, boolean compact) throws IOException
+	{
+		mOut = compact ? new CompactedObjectOutputStream(os) : new ObjectOutputStream(os);
+	}
+
+	public void writeBool(boolean v) throws IOException
+	{
+		mOut.writeBoolean(v);
+	}
+
+	public void writeByte(byte v) throws IOException
+	{
+		mOut.writeByte(v);
+	}
+
+	public void writeShort(short v) throws IOException
+	{
+		mOut.writeShort(v);
+	}
+
+	public void writeInt(int v) throws IOException
+	{
+		mOut.writeInt(v);
+	}
+
+	public void writeLong(long v) throws IOException
+	{
+		mOut.writeLong(v);
+	}
+
+	public void writeFloat(float v) throws IOException
+	{
+		mOut.writeFloat(v);
+	}
+
+	public void writeDouble(double v) throws IOException
+	{
+		mOut.writeDouble(v);
+	}
+
+	public void writeBytes(byte[] b) throws IOException
+	{
+		if( b == null )
+			mOut.writeInt(-1);
+		else
+			this.writeBytes(b, 0, b.length);
+	}
+
+	public void writeBytes(byte[] b, int off, int len) throws IOException
+	{
+		mOut.writeInt(len);
+		mOut.write(b, off, len);
+	}
+
+	public void writeUTF(String v) throws IOException
+	{
+		if( v == null )
+		{
+			mOut.writeInt(-1);
+		}
+		else
+		{
+			mOut.writeInt(v.length());
+			mOut.writeUTF(v);
+		}
+	}
+
+	public void writeObject(Object obj) throws IOException
+	{
+		if( obj == null )
+		{
+			mOut.writeByte(0);
+		}
+		else
+		{
+			mOut.writeByte(1);
+			mOut.writeObject(obj);
+		}
+	}
+
+	public void flushBuffer() throws IOException
+	{
+		mOut.flush();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.java
new file mode 100644
index 0000000..e3b6d4b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.java;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.dubbo.common.serialize.Serialization;

+

+/**

+ * @author ding.lid

+ */

+public class JavaSerialization implements Serialization {

+

+    public byte getContentTypeId() {

+        return 3;

+    }

+

+    public String getContentType() {

+        return "x-application/java";

+    }

+

+    public ObjectOutput serialize(URL url, OutputStream out) throws IOException {

+        return new JavaObjectOutput(out);

+    }

+

+    public ObjectInput deserialize(URL url, InputStream is) throws IOException {

+        return new JavaObjectInput(is);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectInput.java
new file mode 100644
index 0000000..bf38939
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectInput.java
@@ -0,0 +1,138 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.json;

+

+import java.io.BufferedReader;

+import java.io.EOFException;

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.InputStreamReader;

+import java.io.Reader;

+import java.lang.reflect.Type;

+

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.utils.PojoUtils;

+import com.alibaba.fastjson.JSON;

+

+/**

+ * JsonObjectInput

+ * 

+ * @author william.liangf

+ */

+public class FastJsonObjectInput implements ObjectInput {

+

+    private final BufferedReader reader;

+

+    public FastJsonObjectInput(InputStream in){

+        this(new InputStreamReader(in));

+    }

+

+    public FastJsonObjectInput(Reader reader){

+        this.reader = new BufferedReader(reader);

+    }

+

+    public boolean readBool() throws IOException {

+        try {

+            return readObject(boolean.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public byte readByte() throws IOException {

+        try {

+            return readObject( byte.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public short readShort() throws IOException {

+        try {

+            return readObject(short.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public int readInt() throws IOException {

+        try {

+            return readObject(int.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public long readLong() throws IOException {

+        try {

+            return readObject(long.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public float readFloat() throws IOException {

+        try {

+            return readObject(float.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public double readDouble() throws IOException {

+        try {

+            return readObject(double.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public String readUTF() throws IOException {

+        try {

+            return readObject(String.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public byte[] readBytes() throws IOException {

+        return readLine().getBytes();

+    }

+

+    public Object readObject() throws IOException, ClassNotFoundException {

+        String json = readLine();

+        return JSON.parse(json);

+    }

+

+    public <T> T readObject(Class<T> cls) throws IOException, ClassNotFoundException {

+        String json = readLine();

+        return JSON.parseObject(json, cls);

+    }

+

+    @SuppressWarnings("unchecked")

+    public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException

+    {

+        Object value = readObject(cls);

+        return (T) PojoUtils.realize(value, cls, type);

+    }

+

+    private String readLine() throws IOException, EOFException {

+        String line = reader.readLine();

+        if(line == null || line.trim().length() == 0) throw new EOFException();

+        return line;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectOutput.java
new file mode 100644
index 0000000..ffaaf97
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectOutput.java
@@ -0,0 +1,100 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.json;

+

+import java.io.IOException;

+import java.io.OutputStream;

+import java.io.OutputStreamWriter;

+import java.io.PrintWriter;

+import java.io.Writer;

+

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.fastjson.serializer.JSONSerializer;

+import com.alibaba.fastjson.serializer.SerializeWriter;

+import com.alibaba.fastjson.serializer.SerializerFeature;

+

+/**

+ * JsonObjectOutput

+ * 

+ * @author william.liangf

+ */

+public class FastJsonObjectOutput implements ObjectOutput {

+

+    private final PrintWriter writer;

+    

+    public FastJsonObjectOutput(OutputStream out) {

+        this(new OutputStreamWriter(out));

+    }

+    

+    public FastJsonObjectOutput(Writer writer) {

+        this.writer = new PrintWriter(writer);

+    }

+

+    public void writeBool(boolean v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeByte(byte v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeShort(short v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeInt(int v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeLong(long v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeFloat(float v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeDouble(double v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeUTF(String v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeBytes(byte[] b) throws IOException {

+        writer.println(new String(b));

+    }

+

+    public void writeBytes(byte[] b, int off, int len) throws IOException {

+        writer.println(new String(b, off, len));

+    }

+

+    public void writeObject(Object obj) throws IOException {

+        SerializeWriter out = new SerializeWriter();

+        JSONSerializer serializer = new JSONSerializer(out);

+        serializer.config(SerializerFeature.WriteEnumUsingToString, true);

+        serializer.write(obj);

+        out.writeTo(writer);

+        writer.println();

+        writer.flush();

+    }

+

+    public void flushBuffer() throws IOException {

+        writer.flush();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.java
new file mode 100644
index 0000000..28c00e6
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.json;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.dubbo.common.serialize.Serialization;

+

+/**

+ * FastJsonSerialization

+ * 

+ * @author william.liangf

+ */

+public class FastJsonSerialization implements Serialization {

+

+    public byte getContentTypeId() {

+        return 6;

+    }

+

+    public String getContentType() {

+        return "text/json";

+    }

+    

+    public ObjectOutput serialize(URL url, OutputStream output) throws IOException {

+        return new FastJsonObjectOutput(output);

+    }

+

+    public ObjectInput deserialize(URL url, InputStream input) throws IOException {

+        return new FastJsonObjectInput(input);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectInput.java
new file mode 100644
index 0000000..d6724da
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectInput.java
@@ -0,0 +1,158 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.json;

+

+import java.io.BufferedReader;

+import java.io.EOFException;

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.InputStreamReader;

+import java.io.Reader;

+import java.lang.reflect.Type;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.json.JSON;

+import com.alibaba.dubbo.common.json.ParseException;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.utils.PojoUtils;

+

+/**

+ * JsonObjectInput

+ * 

+ * @author william.liangf

+ * @author ding.lid

+ */

+public class JsonObjectInput implements ObjectInput {

+    

+    private final BufferedReader reader;

+

+    public JsonObjectInput(InputStream in){

+        this(new InputStreamReader(in));

+    }

+

+    public JsonObjectInput(Reader reader){

+        this.reader = new BufferedReader(reader);

+    }

+

+    public boolean readBool() throws IOException {

+        try {

+            return readObject(boolean.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public byte readByte() throws IOException {

+        try {

+            return readObject( byte.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public short readShort() throws IOException {

+        try {

+            return readObject(short.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public int readInt() throws IOException {

+        try {

+            return readObject(int.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public long readLong() throws IOException {

+        try {

+            return readObject(long.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public float readFloat() throws IOException {

+        try {

+            return readObject(float.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public double readDouble() throws IOException {

+        try {

+            return readObject(double.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public String readUTF() throws IOException {

+        try {

+            return readObject(String.class);

+        } catch (ClassNotFoundException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    public byte[] readBytes() throws IOException {

+        return readLine().getBytes();

+    }

+

+    public Object readObject() throws IOException, ClassNotFoundException {

+        try {

+            String json = readLine();

+            if (json.startsWith("{")) {

+                return JSON.parse(json, Map.class);

+            } else {

+                json = "{\"value\":" + json + "}";

+                

+                @SuppressWarnings("unchecked")

+                Map<String, Object> map = JSON.parse(json, Map.class);

+                return map.get("value");

+            }

+        } catch (ParseException e) {

+            throw new IOException(e.getMessage());

+        }

+    }

+

+    @SuppressWarnings("unchecked")

+    public <T> T readObject(Class<T> cls) throws IOException, ClassNotFoundException {

+        Object value = readObject();

+        return (T) PojoUtils.realize(value, cls);

+        /*try {

+            return JSON.parse(readLine(), cls);

+        } catch (ParseException e) {

+            throw new IOException(e.getMessage());

+        }*/

+    }

+

+    @SuppressWarnings("unchecked")

+    public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException

+    {

+        Object value = readObject();

+        return (T) PojoUtils.realize(value, cls, type);

+    }

+

+    private String readLine() throws IOException, EOFException {

+        String line = reader.readLine();

+        if(line == null || line.trim().length() == 0) throw new EOFException();

+        return line;

+    }

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectOutput.java
new file mode 100644
index 0000000..53ebc2f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectOutput.java
@@ -0,0 +1,105 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.json;

+

+import java.io.IOException;

+import java.io.OutputStream;

+import java.io.OutputStreamWriter;

+import java.io.PrintWriter;

+import java.io.Writer;

+

+import com.alibaba.dubbo.common.json.JSON;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+

+/**

+ * JsonObjectOutput

+ * 

+ * @author william.liangf

+ */

+public class JsonObjectOutput implements ObjectOutput {

+    

+    private final PrintWriter writer;

+    

+    private final boolean writeClass;

+    

+    public JsonObjectOutput(OutputStream out) {

+        this(new OutputStreamWriter(out), false);

+    }

+    

+    public JsonObjectOutput(Writer writer) {

+        this(writer, false);

+    }

+    

+    public JsonObjectOutput(OutputStream out, boolean writeClass) {

+        this(new OutputStreamWriter(out), writeClass);

+    }

+    

+    public JsonObjectOutput(Writer writer, boolean writeClass) {

+        this.writer = new PrintWriter(writer);

+        this.writeClass = writeClass;

+    }

+

+    public void writeBool(boolean v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeByte(byte v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeShort(short v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeInt(int v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeLong(long v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeFloat(float v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeDouble(double v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeUTF(String v) throws IOException {

+        writeObject(v);

+    }

+

+    public void writeBytes(byte[] b) throws IOException {

+        writer.println(new String(b));

+    }

+

+    public void writeBytes(byte[] b, int off, int len) throws IOException {

+        writer.println(new String(b, off, len));

+    }

+

+    public void writeObject(Object obj) throws IOException {

+        JSON.json(obj, writer, writeClass);

+        writer.println();

+        writer.flush();

+    }

+

+    public void flushBuffer() throws IOException {

+        writer.flush();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.java
new file mode 100644
index 0000000..0628fdd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.support.json;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.dubbo.common.serialize.Serialization;

+

+/**

+ * JsonSerialization

+ * 

+ * @author william.liangf

+ */

+public class JsonSerialization implements Serialization {

+

+    public byte getContentTypeId() {

+        return 5;

+    }

+

+    public String getContentType() {

+        return "text/json";

+    }

+    

+    public ObjectOutput serialize(URL url, OutputStream output) throws IOException {

+        return new JsonObjectOutput(output, url.getParameter("with.class", true));

+    }

+

+    public ObjectInput deserialize(URL url, InputStream input) throws IOException {

+        return new JsonObjectInput(input);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/Status.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/Status.java
new file mode 100644
index 0000000..1029f6f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/Status.java
@@ -0,0 +1,82 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.status;

+

+/**

+ * Status

+ * 

+ * @author william.liangf

+ */

+public class Status {

+    

+    /**

+     * Level

+     */

+    public static enum Level {

+        /**

+         * OK

+         */

+        OK, 

+        

+        /**

+         * WARN

+         */

+        WARN, 

+        

+        /**

+         * ERROR

+         */

+        ERROR, 

+        

+        /**

+         * UNKNOWN

+         */

+        UNKNOWN

+    }

+    

+    private final Level level;

+

+    private final String message;

+

+    private final String description;

+    

+    public Status(Level level){

+        this(level, null, null);

+    }

+

+    public Status(Level level, String message){

+        this(level, message, null);

+    }

+    

+    public Status(Level level, String message, String description){

+        this.level = level;

+        this.message = message;

+        this.description = description;

+    }

+    

+    public Level getLevel() {

+        return level;

+    }

+    

+    public String getMessage() {

+        return message;

+    }

+    

+    public String getDescription() {

+        return description;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java
new file mode 100644
index 0000000..8a95616
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.status;

+

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * StatusChecker

+ * 

+ * @author william.liangf

+ */

+@SPI

+public interface StatusChecker {

+    

+    /**

+     * check status

+     * 

+     * @return status

+     */

+    Status check();

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/LoadStatusChecker.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/LoadStatusChecker.java
new file mode 100644
index 0000000..3b75774
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/LoadStatusChecker.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.status.support;

+

+import java.lang.management.ManagementFactory;

+import java.lang.management.OperatingSystemMXBean;

+import java.lang.reflect.Method;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+

+/**

+ * Load Status

+ * 

+ * @author william.liangf

+ */

+@Activate

+public class LoadStatusChecker implements StatusChecker {

+

+    public Status check() {

+    	OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();

+    	double load;

+    	try {

+    	    Method method = OperatingSystemMXBean.class.getMethod("getSystemLoadAverage", new Class<?>[0]);

+    	    load = (Double)method.invoke(operatingSystemMXBean, new Object[0]);

+    	} catch (Throwable e) {

+    	    load = -1;

+    	}

+    	int cpu = operatingSystemMXBean.getAvailableProcessors();

+        return new Status(load < 0 ? Status.Level.UNKNOWN : (load < cpu ? Status.Level.OK : Status.Level.WARN), (load < 0 ? "" : "load:" + load + ",") + "cpu:" + cpu);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/MemoryStatusChecker.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/MemoryStatusChecker.java
new file mode 100644
index 0000000..cf3ecea
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/MemoryStatusChecker.java
@@ -0,0 +1,41 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.status.support;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+

+/**

+ * MemoryStatus

+ * 

+ * @author william.liangf

+ */

+@Activate

+public class MemoryStatusChecker implements StatusChecker {

+

+    public Status check() {

+        Runtime runtime = Runtime.getRuntime();

+        long freeMemory = runtime.freeMemory();

+        long totalMemory = runtime.totalMemory();

+        long maxMemory = runtime.maxMemory();

+        boolean ok = (maxMemory - (totalMemory - freeMemory) > 2048); // 剩余空间小于2M报警

+        String msg = "max:" + (maxMemory / 1024 / 1024) + "M,total:" 

+        + (totalMemory / 1024 / 1024) + "M,used:" + ((totalMemory / 1024 / 1024) - (freeMemory / 1024 / 1024)) + "M,free:" + (freeMemory / 1024 / 1024) + "M";

+        return new Status(ok ? Status.Level.OK : Status.Level.WARN, msg);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/StatusUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/StatusUtils.java
new file mode 100644
index 0000000..f909499
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/StatusUtils.java
@@ -0,0 +1,56 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.status.support;

+

+import java.util.Map;

+

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.Status.Level;

+

+/**

+ * StatusManager

+ * 

+ * @author william.liangf

+ */

+public class StatusUtils {

+    

+    public static Status getSummaryStatus(Map<String, Status> statuses) {

+        Level level = Level.OK;

+        StringBuilder msg = new StringBuilder();

+        for (Map.Entry<String, Status> entry : statuses.entrySet()) {

+            String key = entry.getKey();

+            Status status = entry.getValue();

+            Level l = status.getLevel();

+            if (Level.ERROR.equals(l)) {

+                level = Level.ERROR;

+                if (msg.length() > 0) {

+                    msg.append(",");

+                }

+                msg.append(key);

+            } else if (Level.WARN.equals(l)) {

+                if(! Level.ERROR.equals(level)) {

+                    level = Level.WARN;

+                }

+                if (msg.length() > 0) {

+                    msg.append(",");

+                }

+                msg.append(key);

+            }

+        }

+        return new Status(level, msg.toString());

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/ThreadPool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/ThreadPool.java
new file mode 100644
index 0000000..57f50a2
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/ThreadPool.java
@@ -0,0 +1,42 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.threadpool;

+

+import java.util.concurrent.Executor;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * ThreadPool

+ * 

+ * @author william.liangf

+ */

+@SPI("fixed")

+public interface ThreadPool {

+    

+    /**

+     * 线程池

+     * 

+     * @param url 线程参数

+     * @return 线程池

+     */

+    @Adaptive({Constants.THREADPOOL_KEY})

+    Executor getExecutor(URL url);

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/AbortPolicyWithReport.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/AbortPolicyWithReport.java
new file mode 100644
index 0000000..4296e5e
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/AbortPolicyWithReport.java
@@ -0,0 +1,54 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.threadpool.support;

+

+import java.util.concurrent.RejectedExecutionException;

+import java.util.concurrent.ThreadPoolExecutor;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+

+/**

+ * Abort Policy.

+ * Log warn info when abort.

+ * 

+ * @author ding.lid

+ */

+public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {

+    

+    protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class);

+    

+    private final String threadName;

+    

+    private final URL url;

+    

+    public AbortPolicyWithReport(String threadName, URL url) {

+        this.threadName = threadName;

+        this.url = url;

+    }

+    

+    @Override

+    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

+        String msg = String.format("Thread pool is EXHAUSTED!" +

+        		" Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d), Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s!" , 

+                threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),

+                e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(), url.toIdentityString());

+        logger.warn(msg);

+        throw new RejectedExecutionException(msg);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.java
new file mode 100644
index 0000000..5d9cdc0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.threadpool.support.cached;

+

+import java.util.concurrent.Executor;

+import java.util.concurrent.LinkedBlockingQueue;

+import java.util.concurrent.SynchronousQueue;

+import java.util.concurrent.ThreadPoolExecutor;

+import java.util.concurrent.TimeUnit;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.threadpool.ThreadPool;

+import com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+

+/**

+ * CachedThreadPool

+ * 

+ * @see java.util.concurrent.Executors#newCachedThreadPool()

+ * @author william.liangf

+ */

+public class CachedThreadPool implements ThreadPool {

+

+    public Executor getExecutor(URL url) {

+        String threadName = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);

+        int threads = url.getParameter(Constants.THREADS_KEY, Integer.MAX_VALUE);

+        int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);

+        int keepalive = url.getParameter(Constants.THREAD_ALIVE_KEY, Constants.DEFAULT_THREAD_ALIVE);

+        return new ThreadPoolExecutor(0, threads, keepalive, TimeUnit.MILLISECONDS, 

+                                      queues <= 0 ? new SynchronousQueue<Runnable>() : new LinkedBlockingQueue<Runnable>(queues),

+                               new NamedThreadFactory(threadName, true), new AbortPolicyWithReport(threadName, url));

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.java
new file mode 100644
index 0000000..4ed95c7
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.threadpool.support.fixed;

+

+import java.util.concurrent.Executor;

+import java.util.concurrent.LinkedBlockingQueue;

+import java.util.concurrent.SynchronousQueue;

+import java.util.concurrent.ThreadPoolExecutor;

+import java.util.concurrent.TimeUnit;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.threadpool.ThreadPool;

+import com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+

+/**

+ * FixedThreadPool

+ * 

+ * @see java.util.concurrent.Executors#newFixedThreadPool(int)

+ * @author william.liangf

+ */

+public class FixedThreadPool implements ThreadPool {

+

+    public Executor getExecutor(URL url) {

+        String threadName = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);

+        int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);

+        int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);

+        return new ThreadPoolExecutor(threads, threads, Constants.DEFAULT_THREAD_ALIVE, TimeUnit.MILLISECONDS, 

+                                      queues <= 0 ? new SynchronousQueue<Runnable>() : new LinkedBlockingQueue<Runnable>(queues),

+                               new NamedThreadFactory(threadName, true), new AbortPolicyWithReport(threadName, url));

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/AtomicPositiveInteger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/AtomicPositiveInteger.java
new file mode 100644
index 0000000..b1ade56
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/AtomicPositiveInteger.java
@@ -0,0 +1,186 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.util.concurrent.atomic.AtomicInteger;

+

+/**

+ * AtomicPositiveInteger

+ * 

+ * @author william.liangf

+ * @author ding.lid

+ */

+public class AtomicPositiveInteger extends Number {

+    

+    private static final long serialVersionUID = -3038533876489105940L;

+    

+    private final AtomicInteger i;

+    

+    public AtomicPositiveInteger() {

+        i = new AtomicInteger();

+    }

+    

+    public AtomicPositiveInteger(int initialValue) {

+        i = new AtomicInteger(initialValue);

+    }

+

+    public final int getAndIncrement() {

+        for (;;) {

+            int current = i.get();

+            int next = (current >= Integer.MAX_VALUE ? 0 : current + 1);

+            if (i.compareAndSet(current, next)) {

+                return current;

+            }

+        }

+    }

+

+    public final int getAndDecrement() {

+        for (;;) {

+            int current = i.get();

+            int next = (current <= 0 ? Integer.MAX_VALUE : current - 1);

+            if (i.compareAndSet(current, next)) {

+                return current;

+            }

+        }

+    }

+

+    public final int incrementAndGet() {

+        for (;;) {

+            int current = i.get();

+            int next = (current >= Integer.MAX_VALUE ? 0 : current + 1);

+            if (i.compareAndSet(current, next)) {

+                return next;

+            }

+        }

+    }

+

+    public final int decrementAndGet() {

+        for (;;) {

+            int current = i.get();

+            int next = (current <= 0 ? Integer.MAX_VALUE : current - 1);

+            if (i.compareAndSet(current, next)) {

+                return next;

+            }

+        }

+    }

+

+    public final int get() {

+        return i.get();

+    }

+

+    public final void set(int newValue) {

+        if (newValue < 0) {

+            throw new IllegalArgumentException("new value " + newValue + " < 0");

+        }

+        i.set(newValue);

+    }

+

+    public final int getAndSet(int newValue) {

+        if (newValue < 0) {

+            throw new IllegalArgumentException("new value " + newValue + " < 0");

+        }

+        return i.getAndSet(newValue);

+    }

+

+    public final int getAndAdd(int delta) {

+        if (delta < 0) {

+            throw new IllegalArgumentException("delta " + delta + " < 0");

+        }

+        for (;;) {

+            int current = i.get();

+            int next = (current >= Integer.MAX_VALUE - delta + 1 ? delta - 1 : current + delta);

+            if (i.compareAndSet(current, next)) {

+                return current;

+            }

+        }

+    }

+

+    public final int addAndGet(int delta) {

+        if (delta < 0) {

+            throw new IllegalArgumentException("delta " + delta + " < 0");

+        }

+        for (;;) {

+            int current = i.get();

+            int next = (current >= Integer.MAX_VALUE - delta + 1 ? delta - 1 : current + delta);

+            if (i.compareAndSet(current, next)) {

+                return next;

+            }

+        }

+    }

+

+    public final boolean compareAndSet(int expect, int update) {

+        if (update < 0) {

+            throw new IllegalArgumentException("update value " + update + " < 0");

+        }

+        return i.compareAndSet(expect, update);

+    }

+

+    public final boolean weakCompareAndSet(int expect, int update) {

+        if (update < 0) {

+            throw new IllegalArgumentException("update value " + update + " < 0");

+        }

+        return i.weakCompareAndSet(expect, update);

+    }

+

+    public byte byteValue() {

+        return i.byteValue();

+    }

+

+    public short shortValue() {

+        return i.shortValue();

+    }

+

+    public int intValue() {

+        return i.intValue();

+    }

+

+    public long longValue() {

+        return i.longValue();

+    }

+

+    public float floatValue() {

+        return i.floatValue();

+    }

+

+    public double doubleValue() {

+        return i.doubleValue();

+    }

+

+    public String toString() {

+        return i.toString();

+    }

+

+    @Override

+    public int hashCode() {

+        final int prime = 31;

+        int result = 1;

+        result = prime * result + ((i == null) ? 0 : i.hashCode());

+        return result;

+    }

+

+    @Override

+    public boolean equals(Object obj) {

+        if (this == obj) return true;

+        if (obj == null) return false;

+        if (getClass() != obj.getClass()) return false;

+        AtomicPositiveInteger other = (AtomicPositiveInteger) obj;

+        if (i == null) {

+            if (other.i != null) return false;

+        } else if (!i.equals(other.i)) return false;

+        return true;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ClassHelper.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ClassHelper.java
new file mode 100644
index 0000000..0c66edd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ClassHelper.java
@@ -0,0 +1,191 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class ClassHelper {
+
+	/**
+	 * get class loader 
+	 * 
+	 * @param cls
+	 * @return class loader
+	 */
+    public static ClassLoader getClassLoader(Class<?> cls) {
+    	ClassLoader cl = null;
+        try {
+            cl = Thread.currentThread().getContextClassLoader();
+        } catch (Throwable ex) {
+            // Cannot access thread context ClassLoader - falling back to system class loader...
+        }
+        if (cl == null) {
+            // No thread context class loader -> use class loader of this class.
+            cl = cls.getClassLoader();
+        }
+        return cl;
+    }
+
+    /**
+     * Return the default ClassLoader to use: typically the thread context
+     * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
+     * class will be used as fallback.
+     * <p>
+     * Call this method if you intend to use the thread context ClassLoader in a
+     * scenario where you absolutely need a non-null ClassLoader reference: for
+     * example, for class path resource loading (but not necessarily for
+     * <code>Class.forName</code>, which accepts a <code>null</code> ClassLoader
+     * reference as well).
+     * 
+     * @return the default ClassLoader (never <code>null</code>)
+     * @see java.lang.Thread#getContextClassLoader()
+     */
+    public static ClassLoader getClassLoader() {
+    	return getClassLoader(ClassHelper.class);
+    }
+
+    /**
+     * Same as <code>Class.forName()</code>, except that it works for primitive
+     * types.
+     */
+    public static Class<?> forName(String name) throws ClassNotFoundException {
+        return forName(name, getClassLoader());
+    }
+
+    /**
+     * Replacement for <code>Class.forName()</code> that also returns Class
+     * instances for primitives (like "int") and array class names (like
+     * "String[]").
+     * 
+     * @param name the name of the Class
+     * @param classLoader the class loader to use (may be <code>null</code>,
+     *            which indicates the default class loader)
+     * @return Class instance for the supplied name
+     * @throws ClassNotFoundException if the class was not found
+     * @throws LinkageError if the class file could not be loaded
+     * @see Class#forName(String, boolean, ClassLoader)
+     */
+    public static Class<?> forName(String name, ClassLoader classLoader)
+            throws ClassNotFoundException, LinkageError {
+
+        Class<?> clazz = resolvePrimitiveClassName(name);
+        if (clazz != null) {
+            return clazz;
+        }
+
+        // "java.lang.String[]" style arrays
+        if (name.endsWith(ARRAY_SUFFIX)) {
+            String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
+            Class<?> elementClass = forName(elementClassName, classLoader);
+            return Array.newInstance(elementClass, 0).getClass();
+        }
+
+        // "[Ljava.lang.String;" style arrays
+        int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX);
+        if (internalArrayMarker != -1 && name.endsWith(";")) {
+            String elementClassName = null;
+            if (internalArrayMarker == 0) {
+                elementClassName = name
+                        .substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1);
+            } else if (name.startsWith("[")) {
+                elementClassName = name.substring(1);
+            }
+            Class<?> elementClass = forName(elementClassName, classLoader);
+            return Array.newInstance(elementClass, 0).getClass();
+        }
+
+        ClassLoader classLoaderToUse = classLoader;
+        if (classLoaderToUse == null) {
+            classLoaderToUse = getClassLoader();
+        }
+        return classLoaderToUse.loadClass(name);
+    }
+
+    /**
+     * Resolve the given class name as primitive class, if appropriate,
+     * according to the JVM's naming rules for primitive classes.
+     * <p>
+     * Also supports the JVM's internal class names for primitive arrays. Does
+     * <i>not</i> support the "[]" suffix notation for primitive arrays; this is
+     * only supported by {@link #forName}.
+     * 
+     * @param name the name of the potentially primitive class
+     * @return the primitive class, or <code>null</code> if the name does not
+     *         denote a primitive class or primitive array class
+     */
+    public static Class<?> resolvePrimitiveClassName(String name) {
+        Class<?> result = null;
+        // Most class names will be quite long, considering that they
+        // SHOULD sit in a package, so a length check is worthwhile.
+        if (name != null && name.length() <= 8) {
+            // Could be a primitive - likely.
+            result = (Class<?>) primitiveTypeNameMap.get(name);
+        }
+        return result;
+    }
+
+    /** Suffix for array class names: "[]" */
+    public static final String  ARRAY_SUFFIX            = "[]";
+    /** Prefix for internal array class names: "[L" */
+    private static final String INTERNAL_ARRAY_PREFIX   = "[L";
+
+    /**
+     * Map with primitive type name as key and corresponding primitive type as
+     * value, for example: "int" -> "int.class".
+     */
+    private static final Map<String,Class<?>>    primitiveTypeNameMap    = new HashMap<String, Class<?>>(16);
+
+    /**
+     * Map with primitive wrapper type as key and corresponding primitive type
+     * as value, for example: Integer.class -> int.class.
+     */
+    private static final Map<Class<?>,Class<?>>    primitiveWrapperTypeMap = new HashMap<Class<?>, Class<?>>(8);
+
+    static {
+        primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
+        primitiveWrapperTypeMap.put(Byte.class, byte.class);
+        primitiveWrapperTypeMap.put(Character.class, char.class);
+        primitiveWrapperTypeMap.put(Double.class, double.class);
+        primitiveWrapperTypeMap.put(Float.class, float.class);
+        primitiveWrapperTypeMap.put(Integer.class, int.class);
+        primitiveWrapperTypeMap.put(Long.class, long.class);
+        primitiveWrapperTypeMap.put(Short.class, short.class);
+
+        Set<Class<?>> primitiveTypeNames = new HashSet<Class<?>>(16);
+        primitiveTypeNames.addAll(primitiveWrapperTypeMap.values());
+        primitiveTypeNames.addAll(Arrays
+                .asList(new Class<?>[] { boolean[].class, byte[].class, char[].class, double[].class,
+            float[].class, int[].class, long[].class, short[].class }));
+        for (Iterator<Class<?>> it = primitiveTypeNames.iterator(); it.hasNext();) {
+            Class<?> primitiveClass = (Class<?>) it.next();
+            primitiveTypeNameMap.put(primitiveClass.getName(), primitiveClass);
+        }
+    }
+
+    public static String toShortString(Object obj){
+        if(obj == null){
+            return "null";
+        }
+        return obj.getClass().getSimpleName() + "@" + System.identityHashCode(obj);
+        
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CollectionUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CollectionUtils.java
new file mode 100644
index 0000000..d3f9234
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CollectionUtils.java
@@ -0,0 +1,188 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.Comparator;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+public class CollectionUtils {

+

+	@SuppressWarnings({ "unchecked", "rawtypes" })

+	public static <T> List<T> sort(List<T> list) {

+		if (list != null && list.size() > 0) {

+			Collections.sort((List)list);

+		}

+		return list;

+	}

+	

+	private static final Comparator<String> SIMPLE_NAME_COMPARATOR = new Comparator<String>() {

+		public int compare(String s1, String s2) {

+			if (s1 == null && s2 == null) {

+				return 0;

+			}

+			if (s1 == null) {

+				return -1;

+			}

+			if (s2 == null) {

+				return 1;

+			}

+			int i1 = s1.lastIndexOf('.');

+			if (i1 >= 0) {

+				s1 = s1.substring(i1 + 1);

+			}

+			int i2 = s2.lastIndexOf('.');

+			if (i2 >= 0) {

+				s2 = s2.substring(i2 + 1);

+			}

+			return s1.compareToIgnoreCase(s2);

+		}

+	};

+	

+	public static List<String> sortSimpleName(List<String> list) {

+		if (list != null && list.size() > 0) {

+			Collections.sort(list, SIMPLE_NAME_COMPARATOR);

+		}

+		return list;

+	}

+

+	public static Map<String, Map<String, String>> splitAll(Map<String, List<String>> list, String separator) {

+		if (list == null) {

+			return null;

+		}

+		Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();

+		for (Map.Entry<String, List<String>> entry : list.entrySet()) {

+			result.put(entry.getKey(), split(entry.getValue(), separator));

+		}

+		return result;

+	}

+	

+	public static Map<String, List<String>> joinAll(Map<String, Map<String, String>> map, String separator) {

+		if (map == null) {

+			return null;

+		}

+		Map<String, List<String>> result = new HashMap<String, List<String>>();

+		for (Map.Entry<String, Map<String, String>> entry : map.entrySet()) {

+			result.put(entry.getKey(), join(entry.getValue(), separator));

+		}

+		return result;

+	}

+

+	public static Map<String, String> split(List<String> list, String separator) {

+		if (list == null) {

+			return null;

+		}

+		Map<String, String> map = new HashMap<String, String>();

+		if (list == null || list.size() == 0) {

+			return map;

+		}

+		for (String item : list) {

+			int index = item.indexOf(separator);

+			if (index == -1) {

+				map.put(item, "");

+			} else {

+				map.put(item.substring(0, index), item.substring(index + 1));

+			}

+		}

+		return map;

+	}

+

+	public static List<String> join(Map<String, String> map, String separator) {

+		if (map == null) {

+			return null;

+		}

+		List<String> list = new ArrayList<String>();

+		if (map == null || map.size() == 0) {

+			return list;

+		}

+		for (Map.Entry<String, String> entry : map.entrySet()) {

+			String key = entry.getKey();

+			String value = entry.getValue();

+			if (value == null || value.length() == 0) {

+				list.add(key);

+			} else {

+				list.add(key + separator + value);

+			}

+		}

+		return list;

+	}

+

+	public static boolean mapEquals(Map<?, ?> map1, Map<?, ?> map2) {

+		if (map1 == null && map2 == null) {

+			return true;

+		}

+		if (map1 == null || map2 == null) {

+			return false;

+		}

+		if (map1.size() != map2.size()) {

+			return false;

+		}

+		for (Map.Entry<?, ?> entry : map1.entrySet()) {

+			Object key = entry.getKey();

+			Object value1 = entry.getValue();

+			Object value2 = map2.get(key);

+			if (! objectEquals(value1, value2)) {

+				return false;

+			}

+		}

+		return true;

+	}

+	

+	private static boolean objectEquals(Object obj1, Object obj2) {

+		if (obj1 == null && obj2 == null) {

+			return true;

+		}

+		if (obj1 == null || obj2 == null) {

+			return false;

+		}

+		return obj1.equals(obj2);

+	}

+	

+	public static Map<String, String> toStringMap(String... pairs) {

+        Map<String, String> parameters = new HashMap<String, String>();

+        if (pairs.length > 0) {

+            if (pairs.length % 2 != 0) {

+                throw new IllegalArgumentException("pairs must be even.");

+            }

+            for (int i = 0; i < pairs.length; i = i + 2) {

+                parameters.put(pairs[i], pairs[i + 1]);

+            }

+        }

+        return parameters;

+    }

+

+	@SuppressWarnings("unchecked")

+    public static <K, V> Map<K, V> toMap(Object ... pairs) {

+	    Map<K, V> ret = new HashMap<K, V>();

+	    if (pairs == null || pairs.length == 0) return ret;

+	

+        if (pairs.length % 2 != 0) {

+            throw new IllegalArgumentException("Map pairs can not be odd number.");

+        }        

+        int len = pairs.length / 2;

+        for (int i = 0; i < len; i ++) {

+            ret.put((K) pairs[2 * i], (V) pairs[2 * i + 1]);

+        }

+	    return ret;

+	}

+	

+	private CollectionUtils() {

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtils.java
new file mode 100644
index 0000000..877f649
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtils.java
@@ -0,0 +1,139 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import java.lang.reflect.Array;

+import java.math.BigDecimal;

+import java.math.BigInteger;

+import java.text.ParseException;

+import java.text.SimpleDateFormat;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Date;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Set;

+
+/**
+ * @author ding.lid
+ */
+public class CompatibleTypeUtils {

+    
+    private CompatibleTypeUtils() {
+    }
+
+    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+    /**
+     * 兼容类型转换。null值是OK的。如果不需要转换,则返回原来的值。
+     * 进行的兼容类型转换如下:(基本类对应的Wrapper类型不再列出。)
+     * <ul>
+     * <li> String -> char, enum, Date
+     * <li> byte, short, int, long -> byte, short, int, long
+     * <li> float, double -> float, double
+     * </ul>
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+	public static Object compatibleTypeConvert(Object value, Class<?> type) {
+        if(value == null || type == null || type.isAssignableFrom(value.getClass())) {
+        	return value;
+        }
+        if(value instanceof String) {

+            String string = (String) value;

+            if (char.class.equals(type) || Character.class.equals(type)) {

+                if(string.length() != 1) {

+                    throw new IllegalArgumentException(String.format("CAN NOT convert String(%s) to char!" +

+                            " when convert String to char, the String MUST only 1 char.", string));

+                }

+                return string.charAt(0);

+            } else if(type.isEnum()) {

+                return Enum.valueOf((Class<Enum>)type, string);

+            } else if(type == BigInteger.class) {

+                return new BigInteger(string);

+            } else if(type == BigDecimal.class) {

+                return new BigDecimal(string);

+            } else if(type == Date.class) {

+                try {

+                    return new SimpleDateFormat(DATE_FORMAT).parse((String) value);

+                } catch (ParseException e) {

+                    throw new IllegalStateException("Failed to parse date " + value + " by format " + DATE_FORMAT + ", cause: " + e.getMessage(), e);

+                }

+            }
+        } else if(value instanceof Number) {

+            Number number = (Number) value;

+            if (type == byte.class || type == Byte.class) {

+                return number.byteValue();

+            } else if (type == short.class || type == Short.class) {

+                return number.shortValue();

+            } else if (type == int.class || type == Integer.class) {

+                return number.intValue();

+            } else if (type == long.class || type == Long.class) {

+                return number.longValue();

+            } else if (type == float.class || type == Float.class) {

+                return number.floatValue();

+            } else if (type == double.class || type == Double.class) {

+                return number.doubleValue();

+            } else if (type == BigInteger.class) {

+                return BigInteger.valueOf(number.longValue());

+            } else if (type == BigDecimal.class) {

+                return BigDecimal.valueOf(number.doubleValue());

+            } else if (type == Date.class) {

+                return new Date(number.longValue());

+            }
+        } else if(value instanceof Collection) {

+            Collection collection = (Collection) value;

+            if (type.isArray()) {

+                int length = collection.size();

+                Object array = Array.newInstance(type.getComponentType(), length);

+                int i = 0;

+                for (Object item : collection) {

+                    Array.set(array, i ++, item);

+                }

+                return array;

+            } else if (! type.isInterface()) {

+                try {

+                    Collection result = (Collection) type.newInstance();

+                    result.addAll(collection);

+                    return result;

+                } catch (Throwable e) {

+                }

+            } else if (type == List.class) {

+                return new ArrayList<Object>(collection);

+            } else if (type == Set.class) {

+                return new HashSet<Object>(collection);

+            }

+        } else if(value.getClass().isArray() && Collection.class.isAssignableFrom(type)) {

+            Collection collection;

+            if (! type.isInterface()) {

+                try {

+                    collection = (Collection) type.newInstance();

+                } catch (Throwable e) {

+                    collection = new ArrayList<Object>();

+                }

+            } else if (type == Set.class) {

+                collection = new HashSet<Object>();

+            } else {

+                collection = new ArrayList<Object>();

+            }

+            int length = Array.getLength(value);

+            for (int i = 0; i < length; i ++) {

+                collection.add(Array.get(value, i));

+            }

+            return collection;

+        }
+        return value;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ConcurrentHashSet.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ConcurrentHashSet.java
new file mode 100644
index 0000000..7714efc
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ConcurrentHashSet.java
@@ -0,0 +1,124 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.util.AbstractSet;

+import java.util.ConcurrentModificationException;

+import java.util.Iterator;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+

+public class ConcurrentHashSet<E> extends AbstractSet<E> implements Set<E>, java.io.Serializable {

+

+	private static final long serialVersionUID = -8672117787651310382L;

+

+	private static final Object PRESENT = new Object();

+

+	private final ConcurrentHashMap<E, Object> map;

+	

+	public ConcurrentHashSet(){

+	    map = new ConcurrentHashMap<E, Object>();

+	}

+

+    public ConcurrentHashSet(int initialCapacity){

+        map = new ConcurrentHashMap<E, Object>(initialCapacity);

+    }

+

+	/**

+	 * Returns an iterator over the elements in this set. The elements are

+	 * returned in no particular order.

+	 * 

+	 * @return an Iterator over the elements in this set

+	 * @see ConcurrentModificationException

+	 */

+	public Iterator<E> iterator() {

+		return map.keySet().iterator();

+	}

+

+	/**

+	 * Returns the number of elements in this set (its cardinality).

+	 * 

+	 * @return the number of elements in this set (its cardinality)

+	 */

+	public int size() {

+		return map.size();

+	}

+

+	/**

+	 * Returns <tt>true</tt> if this set contains no elements.

+	 * 

+	 * @return <tt>true</tt> if this set contains no elements

+	 */

+	public boolean isEmpty() {

+		return map.isEmpty();

+	}

+

+	/**

+	 * Returns <tt>true</tt> if this set contains the specified element. More

+	 * formally, returns <tt>true</tt> if and only if this set contains an

+	 * element <tt>e</tt> such that

+	 * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.

+	 * 

+	 * @param o

+	 *            element whose presence in this set is to be tested

+	 * @return <tt>true</tt> if this set contains the specified element

+	 */

+	public boolean contains(Object o) {

+		return map.containsKey(o);

+	}

+

+	/**

+	 * Adds the specified element to this set if it is not already present. More

+	 * formally, adds the specified element <tt>e</tt> to this set if this set

+	 * contains no element <tt>e2</tt> such that

+	 * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>. If this

+	 * set already contains the element, the call leaves the set unchanged and

+	 * returns <tt>false</tt>.

+	 * 

+	 * @param e

+	 *            element to be added to this set

+	 * @return <tt>true</tt> if this set did not already contain the specified

+	 *         element

+	 */

+	public boolean add(E e) {

+		return map.put(e, PRESENT) == null;

+	}

+

+	/**

+	 * Removes the specified element from this set if it is present. More

+	 * formally, removes an element <tt>e</tt> such that

+	 * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>, if this

+	 * set contains such an element. Returns <tt>true</tt> if this set contained

+	 * the element (or equivalently, if this set changed as a result of the

+	 * call). (This set will not contain the element once the call returns.)

+	 * 

+	 * @param o

+	 *            object to be removed from this set, if present

+	 * @return <tt>true</tt> if the set contained the specified element

+	 */

+	public boolean remove(Object o) {

+		return map.remove(o) == PRESENT;

+	}

+

+	/**

+	 * Removes all of the elements from this set. The set will be empty after

+	 * this call returns.

+	 */

+	public void clear() {

+		map.clear();

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ConfigUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ConfigUtils.java
new file mode 100644
index 0000000..dc5035c
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ConfigUtils.java
@@ -0,0 +1,280 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.io.FileInputStream;

+import java.io.InputStream;

+import java.util.ArrayList;

+import java.util.Enumeration;

+import java.util.List;

+import java.util.Map;

+import java.util.Properties;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+

+/**

+ * @author ding.lid

+ * @author william.liangf

+ */

+public class ConfigUtils {

+    

+    private static final Logger logger = LoggerFactory.getLogger(ConfigUtils.class);

+    

+    public static boolean isNotEmpty(String value) {

+        return ! isEmpty(value);

+    }

+	

+	public static boolean isEmpty(String value) {

+		return value == null || value.length() == 0 

+    			|| "false".equalsIgnoreCase(value) 

+    			|| "0".equalsIgnoreCase(value) 

+    			|| "null".equalsIgnoreCase(value) 

+    			|| "N/A".equalsIgnoreCase(value);

+	}

+	

+	public static boolean isDefault(String value) {

+		return "true".equalsIgnoreCase(value) 

+				|| "default".equalsIgnoreCase(value);

+	}

+	

+	/**

+	 * 扩展点列表中插入缺省扩展点。

+	 * <p>

+	 * 扩展点列表支持<ul>

+	 * <li>特殊值<code><strong>default</strong></code>,表示缺省扩展点插入的位置

+	 * <li>特殊符号<code><strong>-</strong></code>,表示剔除。 <code>-foo1</code>,剔除添加缺省扩展点foo1。<code>-default</code>,剔除添加所有缺省扩展点。

+	 * </ul>

+	 * 

+	 * @param type 扩展点类型

+	 * @param cfg 扩展点名列表

+	 * @param def 缺省的扩展点的列表

+	 * @return 完成缺省的扩展点列表插入后的列表

+	 */

+	public static List<String> mergeValues(Class<?> type, String cfg, List<String> def) {

+	    List<String> defaults = new ArrayList<String>();

+        if (def != null) {

+            for (String name : def) {

+                if (ExtensionLoader.getExtensionLoader(type).hasExtension(name)) {

+                    defaults.add(name);

+                }

+            }

+        }

+        

+	    List<String> names = new ArrayList<String>();

+	    

+	    // 加入初始值

+        String[] configs = (cfg == null || cfg.trim().length() == 0) ? new String[0] : Constants.COMMA_SPLIT_PATTERN.split(cfg);

+        for (String config : configs) {

+            if(config != null && config.trim().length() > 0) {

+                names.add(config);

+            }

+        }

+

+        // 不包含 -default

+        if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {

+            // 加入 插入缺省扩展点

+            int i = names.indexOf(Constants.DEFAULT_KEY);

+            if (i > 0) {

+                names.addAll(i, defaults);

+            } else {

+                names.addAll(0, defaults);

+            }

+            names.remove(Constants.DEFAULT_KEY);

+        }

+        else {

+            names.remove(Constants.DEFAULT_KEY);

+        }

+        

+        // 合并-的配置项

+        for (String name : new ArrayList<String>(names)) {

+            if (name.startsWith(Constants.REMOVE_VALUE_PREFIX)) {

+                names.remove(name);

+                names.remove(name.substring(1));

+            }

+        }

+        return names;

+	}

+

+    private static Pattern VARIABLE_PATTERN = Pattern.compile(

+            "\\$\\s*\\{?\\s*([\\._0-9a-zA-Z]+)\\s*\\}?");

+    

+	public static String replaceProperty(String expression, Map<String, String> params) {

+        if (expression == null || expression.length() == 0 || expression.indexOf('$') < 0) {

+            return expression;

+        }

+        Matcher matcher = VARIABLE_PATTERN.matcher(expression);

+        StringBuffer sb = new StringBuffer();

+        while (matcher.find()) { // 逐个匹配

+            String key = matcher.group(1);

+            String value = System.getProperty(key);

+            if (value == null && params != null) {

+                value = params.get(key);

+            }

+            if (value == null) {

+                value = "";

+            }

+            matcher.appendReplacement(sb, Matcher.quoteReplacement(value));

+        }

+        matcher.appendTail(sb);

+        return sb.toString();

+    }

+	

+    private static volatile Properties PROPERTIES;

+    

+    public static Properties getProperties() {

+        if (PROPERTIES == null) {

+            synchronized (ConfigUtils.class) {

+                if (PROPERTIES == null) {

+                    String path = System.getProperty(Constants.DUBBO_PROPERTIES_KEY);

+                    if (path == null || path.length() == 0) {

+                        path = System.getenv(Constants.DUBBO_PROPERTIES_KEY);

+                        if (path == null || path.length() == 0) {

+                            path = Constants.DEFAULT_DUBBO_PROPERTIES;

+                        }

+                    }

+                    PROPERTIES = ConfigUtils.loadProperties(path, false, true);

+                }

+            }

+        }

+        return PROPERTIES;

+    }

+    

+    public static void addProperties(Properties properties) {

+        if (properties != null) {

+            getProperties().putAll(properties);

+        }

+    }

+    

+    public static void setProperties(Properties properties) {

+        if (properties != null) {

+            PROPERTIES = properties;

+        }

+    }

+    

+	public static String getProperty(String key) {

+	    return getProperty(key, null);

+	}

+	

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public static String getProperty(String key, String defaultValue) {

+        String value = System.getProperty(key);

+        if (value != null && value.length() > 0) {

+            return value;

+        }

+        Properties properties = getProperties();

+        return replaceProperty(properties.getProperty(key, defaultValue), (Map)properties);

+    }

+    

+    public static Properties loadProperties(String fileName) {

+        return loadProperties(fileName, false, false);

+    }

+    

+    public static Properties loadProperties(String fileName, boolean allowMultiFile) {

+        return loadProperties(fileName, allowMultiFile, false);

+    }

+    

+	/**

+	 * Load properties file to {@link Properties} from class path.

+	 * 

+	 * @param fileName properties file name. for example: <code>dubbo.properties</code>, <code>METE-INF/conf/foo.properties</code>

+	 * @param allowMultiFile if <code>false</code>, throw {@link IllegalStateException} when found multi file on the class path. 

+	 * @return loaded {@link Properties} content. <ul>

+	 * <li>return empty Properties if no file found.

+	 * <li>merge multi properties file if found multi file

+	 * </ul>

+	 * @throws IllegalStateException not allow multi-file, but multi-file exsit on class path.

+	 */

+    public static Properties loadProperties(String fileName, boolean allowMultiFile, boolean allowEmptyFile) {

+        Properties properties = new Properties();

+        if (fileName.startsWith("/")) {

+            try {

+                FileInputStream input = new FileInputStream(fileName);

+                try {

+                    properties.load(input);

+                } finally {

+                    input.close();

+                }

+            } catch (Throwable e) {

+                logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);

+            }

+            return properties;

+        }

+        

+        List<java.net.URL> list = new ArrayList<java.net.URL>();

+        try {

+            Enumeration<java.net.URL> urls = ClassHelper.getClassLoader().getResources(fileName);

+            list = new ArrayList<java.net.URL>();

+            while (urls.hasMoreElements()) {

+                list.add(urls.nextElement());

+            }

+        } catch (Throwable t) {

+            logger.warn("Fail to load " + fileName + " file: " + t.getMessage(), t);

+        }

+        

+        if(list.size() == 0) {

+            if (allowEmptyFile) {

+                logger.warn("No " + fileName + " found on the class path.");

+            }

+            return properties;

+        }

+        

+        if(! allowMultiFile) {

+            if (list.size() > 1) {

+                String errMsg = String.format("only 1 %s file is expected, but %d dubbo.properties files found on class path: %s",

+                        fileName, list.size(), list.toString());

+                logger.warn(errMsg);

+                // throw new IllegalStateException(errMsg); // see http://code.alibabatech.com/jira/browse/DUBBO-133

+            }

+            try {

+                properties.load(ClassHelper.getClassLoader().getResourceAsStream(fileName));

+            } catch (Throwable e) {

+                logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);

+            }

+            return properties;

+        }

+        

+        logger.info("load " + fileName + " properties file from " + list);

+        

+        for(java.net.URL url : list) {

+            try {

+                Properties p = new Properties();

+                InputStream input = url.openStream();

+                if (input != null) {

+                    try {

+                        p.load(input);

+                        properties.putAll(p);

+                    } finally {

+                        try {

+                            input.close();

+                        } catch (Throwable t) {}

+                    }

+                }

+            } catch (Throwable e) {

+                logger.warn("Fail to load " + fileName + " file from " + url + "(ingore this file): " + e.getMessage(), e);

+            }

+        }

+        

+        return properties;

+    }

+

+	private ConfigUtils() {}

+	

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/DubboAppender.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/DubboAppender.java
new file mode 100644
index 0000000..14c3968
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/DubboAppender.java
@@ -0,0 +1,59 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.apache.log4j.ConsoleAppender;

+import org.apache.log4j.spi.LoggingEvent;

+

+public class DubboAppender extends ConsoleAppender {

+

+    public static boolean   available = false;

+

+    public static List<Log> logList   = new ArrayList<Log>();

+

+    public static void doStart() {

+        available = true;

+    }

+    

+    public static void doStop() {

+        available = false;

+    }

+    

+    public static void clear() {

+        logList.clear();

+    }

+

+    public void append(LoggingEvent event) {

+        super.append(event);

+        if (available == true) {

+            Log temp = parseLog(event);

+            logList.add(temp);

+        }

+    }

+

+    private Log parseLog(LoggingEvent event) {

+        Log log = new Log();

+        log.setLogName(event.getLogger().getName());

+        log.setLogLevel(event.getLevel());

+        log.setLogThread(event.getThreadName());

+        log.setLogMessage(event.getMessage().toString());

+        return log;

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java
new file mode 100644
index 0000000..0de8305
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java
@@ -0,0 +1,112 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class ExecutorUtil {
+    private static final Logger logger = LoggerFactory.getLogger(ExecutorUtil.class);
+    private static final ThreadPoolExecutor shutdownExecutor = new ThreadPoolExecutor(0, 1,
+            0L, TimeUnit.MILLISECONDS,
+            new LinkedBlockingQueue<Runnable>(100),
+            new NamedThreadFactory("Close-ExecutorService-Timer", true)); 
+
+    public static boolean isShutdown(Executor executor) {
+        if (executor instanceof ExecutorService) {
+            if (((ExecutorService) executor).isShutdown()) {
+                return true;
+            }
+        }
+        return false;
+    }
+    public static void gracefulShutdown(Executor executor, int timeout) {
+        if (!(executor instanceof ExecutorService) || isShutdown(executor)) {
+            return;
+        }
+        final ExecutorService es = (ExecutorService) executor;
+        try {
+            es.shutdown(); // Disable new tasks from being submitted
+        } catch (SecurityException ex2) {
+            return ;
+        } catch (NullPointerException ex2) {
+            return ;
+        }
+        try {
+            if(! es.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {
+                es.shutdownNow();
+            }
+        } catch (InterruptedException ex) {
+            es.shutdownNow();
+            Thread.currentThread().interrupt();
+        }
+        if (!isShutdown(es)){
+            newThreadToCloseExecutor(es);
+        }
+    }
+    public static void shutdownNow(Executor executor, final int timeout) {
+        if (!(executor instanceof ExecutorService) || isShutdown(executor)) {
+            return;
+        }
+        final ExecutorService es = (ExecutorService) executor;
+        try {
+            es.shutdownNow();
+        } catch (SecurityException ex2) {
+            return ;
+        } catch (NullPointerException ex2) {
+            return ;
+        }
+        try {
+            es.awaitTermination(timeout, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException ex) {
+            Thread.currentThread().interrupt();
+        }
+        if (!isShutdown(es)){
+            newThreadToCloseExecutor(es);
+        }
+    }
+
+    private static void newThreadToCloseExecutor(final ExecutorService es) {
+        if (!isShutdown(es)) {
+            shutdownExecutor.execute(new Runnable() {
+                public void run() {
+                    try {

+                        for (int i=0;i<1000;i++){
+                            es.shutdownNow();

+                            if (es.awaitTermination(10, TimeUnit.MILLISECONDS)){

+                                break;

+                            }

+                        }
+                    } catch (InterruptedException ex) {

+                        Thread.currentThread().interrupt();

+                    } catch (Throwable e) {

+                        logger.warn(e.getMessage(), e);

+                    } 
+                }
+            });
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/IOUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/IOUtils.java
new file mode 100644
index 0000000..1049adf
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/IOUtils.java
@@ -0,0 +1,235 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+public class IOUtils
+{
+	private static final int BUFFER_SIZE = 1024 * 8;
+
+	private IOUtils() {}
+
+	/**
+	 * write.
+	 * 
+	 * @param is InputStream instance.
+	 * @param os OutputStream instance.
+	 * @return count.
+	 * @throws IOException.
+	 */
+	public static long write(InputStream is, OutputStream os) throws IOException
+	{
+		return write(is, os, BUFFER_SIZE);
+	}
+
+	/**
+	 * write.
+	 * 
+	 * @param is InputStream instance.
+	 * @param os OutputStream instance.
+	 * @param bufferSize buffer size.
+	 * @return count.
+	 * @throws IOException.
+	 */
+	public static long write(InputStream is, OutputStream os, int bufferSize) throws IOException
+	{
+		int read;
+		long total = 0;
+		byte[] buff = new byte[bufferSize];
+		while( is.available() > 0 )
+		{
+			read = is.read(buff, 0, buff.length);
+			if( read > 0 )
+			{
+				os.write(buff, 0, read);
+				total += read;
+			}
+		}
+		return total;
+	}
+
+	/**
+	 * read string.
+	 * 
+	 * @param reader Reader instance.
+	 * @return String.
+	 * @throws IOException.
+	 */
+	public static String read(Reader reader) throws IOException
+	{
+		StringWriter writer = new StringWriter();
+		try
+		{
+			write(reader, writer);
+			return writer.getBuffer().toString();
+		}
+		finally{ writer.close(); }
+	}
+
+	/**
+	 * write string.
+	 * 
+	 * @param writer Writer instance.
+	 * @param string String.
+	 * @throws IOException.
+	 */
+	public static long write(Writer writer, String string) throws IOException
+	{
+		Reader reader = new StringReader(string);
+		try{ return write(reader, writer); }finally{ reader.close(); }
+	}
+
+	/**
+	 * write.
+	 * 
+	 * @param reader Reader.
+	 * @param writer Writer.
+	 * @return count.
+	 * @throws IOException.
+	 */
+	public static long write(Reader reader, Writer writer) throws IOException
+	{
+		return write(reader, writer, BUFFER_SIZE);
+	}
+
+	/**
+	 * write.
+	 * 
+	 * @param reader Reader.
+	 * @param writer Writer.
+	 * @param bufferSize buffer size.
+	 * @return count.
+	 * @throws IOException.
+	 */
+	public static long write(Reader reader, Writer writer, int bufferSize) throws IOException
+	{
+		int read;
+		long total = 0;
+		char[] buf = new char[BUFFER_SIZE];
+		while( ( read = reader.read(buf) ) != -1 )
+		{
+			writer.write(buf, 0, read);
+			total += read;
+		}
+		return total;
+	}
+
+	/**
+	 * read lines.
+	 * 
+	 * @param file file.
+	 * @return lines.
+	 * @throws IOException.
+	 */
+	public static String[] readLines(File file) throws IOException
+	{
+		if( file == null || !file.exists() || !file.canRead() )
+	        return new String[0];
+
+		return readLines(new FileInputStream(file));
+	}
+
+	/**
+	 * read lines.
+	 * 
+	 * @param is input stream.
+	 * @return lines.
+	 * @throws IOException.
+	 */
+	public static String[] readLines(InputStream is) throws IOException
+	{
+		List<String> lines = new ArrayList<String>();
+		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+		try
+		{
+			String line;
+			while( (line = reader.readLine()) != null )
+				lines.add(line);
+			return lines.toArray(new String[0]);
+	    }
+		finally
+		{
+			reader.close();
+		}
+	}
+
+	/**
+	 * write lines.
+	 * 
+	 * @param os output stream.
+	 * @param lines lines.
+	 * @throws IOException.
+	 */
+	public static void writeLines(OutputStream os, String[] lines) throws IOException
+	{
+		PrintWriter writer = new PrintWriter(new OutputStreamWriter(os));
+		try
+		{
+			for( String line : lines )
+				writer.println(line);
+			writer.flush();
+		}
+		finally
+		{
+			writer.close();
+		}
+	}
+
+	/**
+	 * write lines.
+	 * 
+	 * @param file file.
+	 * @param lines lines.
+	 * @throws IOException.
+	 */
+	public static void writeLines(File file, String[] lines) throws IOException
+	{
+		if( file == null )
+	        throw new IOException("File is null.");
+		writeLines(new FileOutputStream(file), lines);
+    }
+
+    /**
+     * append lines.
+     * 
+     * @param file file.
+     * @param lines lines.
+     * @throws IOException.
+     */
+    public static void appendLines(File file, String[] lines) throws IOException
+    {
+        if( file == null )
+            throw new IOException("File is null.");
+        writeLines(new FileOutputStream(file, true), lines);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LRUCache.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LRUCache.java
new file mode 100644
index 0000000..9f0deda
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LRUCache.java
@@ -0,0 +1,116 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import java.util.LinkedHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class LRUCache<K, V> extends LinkedHashMap<K, V> {
+
+	private static final long serialVersionUID = -5167631809472116969L;
+
+	private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+	private static final int DEFAULT_MAX_CAPACITY = 1000;
+
+	private volatile int maxCapacity;
+
+	private final Lock lock = new ReentrantLock();
+
+    public LRUCache() {
+    	this(DEFAULT_MAX_CAPACITY);
+    }
+
+    public LRUCache(int maxCapacity) {
+        super(16, DEFAULT_LOAD_FACTOR, true);
+        this.maxCapacity = maxCapacity;
+    }
+
+    @Override
+    protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
+        return size() > maxCapacity;
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        try {
+            lock.lock();
+            return super.containsKey(key);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    @Override
+    public V get(Object key) {
+        try {
+            lock.lock();
+            return super.get(key);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    @Override
+    public V put(K key, V value) {
+        try {
+            lock.lock();
+            return super.put(key, value);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    @Override
+    public V remove(Object key) {
+        try {
+            lock.lock();
+            return super.remove(key);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    @Override
+    public int size() {
+        try {
+            lock.lock();
+            return super.size();
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    @Override
+    public void clear() {
+        try {
+            lock.lock();
+            super.clear();
+        } finally {
+            lock.unlock();
+        }
+    }
+
+	public int getMaxCapacity() {
+		return maxCapacity;
+	}
+
+	public void setMaxCapacity(int maxCapacity) {
+		this.maxCapacity = maxCapacity;
+	}
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Log.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Log.java
new file mode 100644
index 0000000..477b6ce
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Log.java
@@ -0,0 +1,102 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.io.Serializable;

+

+import org.apache.log4j.Level;

+

+/**

+ * Log.java

+ * 

+ * @author tony.chenl

+ */

+public class Log implements Serializable {

+

+    /**

+     * 

+     */

+    private static final long serialVersionUID = -534113138054377073L;

+    private String logName;

+    private Level logLevel;

+    private String logMessage;

+    private String logThread;

+    

+    public String getLogName() {

+        return logName;

+    }

+    

+    public void setLogName(String logName) {

+        this.logName = logName;

+    }

+    

+    public Level getLogLevel() {

+        return logLevel;

+    }

+    

+    public void setLogLevel(Level logLevel) {

+        this.logLevel = logLevel;

+    }

+    

+    public String getLogMessage() {

+        return logMessage;

+    }

+    

+    public void setLogMessage(String logMessage) {

+        this.logMessage = logMessage;

+    }

+    

+    public String getLogThread() {

+        return logThread;

+    }

+    

+    public void setLogThread(String logThread) {

+        this.logThread = logThread;

+    }

+

+    @Override

+    public int hashCode() {

+        final int prime = 31;

+        int result = 1;

+        result = prime * result + ((logLevel == null) ? 0 : logLevel.hashCode());

+        result = prime * result + ((logMessage == null) ? 0 : logMessage.hashCode());

+        result = prime * result + ((logName == null) ? 0 : logName.hashCode());

+        result = prime * result + ((logThread == null) ? 0 : logThread.hashCode());

+        return result;

+    }

+

+    @Override

+    public boolean equals(Object obj) {

+        if (this == obj) return true;

+        if (obj == null) return false;

+        if (getClass() != obj.getClass()) return false;

+        Log other = (Log) obj;

+        if (logLevel == null) {

+            if (other.logLevel != null) return false;

+        } else if (!logLevel.equals(other.logLevel)) return false;

+        if (logMessage == null) {

+            if (other.logMessage != null) return false;

+        } else if (!logMessage.equals(other.logMessage)) return false;

+        if (logName == null) {

+            if (other.logName != null) return false;

+        } else if (!logName.equals(other.logName)) return false;

+        if (logThread == null) {

+            if (other.logThread != null) return false;

+        } else if (!logThread.equals(other.logThread)) return false;

+        return true;

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LogUtil.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LogUtil.java
new file mode 100644
index 0000000..db20463
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LogUtil.java
@@ -0,0 +1,124 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.util.Iterator;

+import java.util.List;

+

+import org.apache.log4j.Level;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+

+/**

+ * TODO Comment of LogTest

+ * 

+ * @author tony.chenl

+ */

+public class LogUtil {

+

+    private static Logger Log = LoggerFactory.getLogger(LogUtil.class);

+

+    public static void start() {

+        DubboAppender.doStart();

+    }

+    

+    public static void stop() {

+        DubboAppender.doStop();

+    }

+

+    public static boolean checkNoError() {

+        if (findLevel(Level.ERROR) == 0) {

+            return true;

+        } else {

+            return false;

+        }

+

+    }

+

+    public static int findName(String expectedLogName) {

+        int count = 0;

+        List<Log> logList = DubboAppender.logList;

+        for (int i = 0; i < logList.size(); i++) {

+            String logName = logList.get(i).getLogName();

+            if (logName.contains(expectedLogName)) count++;

+        }

+        return count;

+    }

+

+    public static int findLevel(Level expectedLevel) {

+        int count = 0;

+        List<Log> logList = DubboAppender.logList;

+        for (int i = 0; i < logList.size(); i++) {

+            Level logLevel = logList.get(i).getLogLevel();

+            if (logLevel.equals(expectedLevel)) count++;

+        }

+        return count;

+    }

+    

+    public static int findLevelWithThreadName(Level expectedLevel,String threadName) {

+        int count = 0;

+        List<Log> logList = DubboAppender.logList;

+        for (int i = 0; i < logList.size(); i++) {

+            Log log = logList.get(i);

+            if (log.getLogLevel().equals(expectedLevel) && log.getLogThread().equals(threadName)) 

+                count++;

+        }

+        return count;

+    }

+

+    public static int findThread(String expectedThread) {

+        int count = 0;

+        List<Log> logList = DubboAppender.logList;

+        for (int i = 0; i < logList.size(); i++) {

+            String logThread = logList.get(i).getLogThread();

+            if (logThread.contains(expectedThread)) count++;

+        }

+        return count;

+    }

+

+    public static int findMessage(String expectedMessage) {

+        int count = 0;

+        List<Log> logList = DubboAppender.logList;

+        for (int i = 0; i < logList.size(); i++) {

+            String logMessage = logList.get(i).getLogMessage();

+            if (logMessage.contains(expectedMessage)) count++;

+        }

+        return count;

+    }

+    

+    public static int findMessage(Level expectedLevel, String expectedMessage) {

+        int count = 0;

+        List<Log> logList = DubboAppender.logList;

+        for (int i = 0; i < logList.size(); i++) {

+            Level logLevel = logList.get(i).getLogLevel();

+            if (logLevel.equals(expectedLevel)) {

+                String logMessage = logList.get(i).getLogMessage();

+                if (logMessage.contains(expectedMessage)) count++;

+            }

+        }

+        return count;

+    }

+

+    public static <T> void printList(List<T> list) {

+        Log.info("PrintList:");

+        Iterator<T> it = list.iterator();

+        while (it.hasNext()) {

+            Log.info(it.next().toString());

+        }

+

+    }

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/NamedThreadFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/NamedThreadFactory.java
new file mode 100755
index 0000000..b9da491
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/NamedThreadFactory.java
@@ -0,0 +1,69 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.util.concurrent.ThreadFactory;

+import java.util.concurrent.atomic.AtomicInteger;

+

+/**

+ * InternalThreadFactory.

+ * 

+ * @author qian.lei

+ */

+

+public class NamedThreadFactory implements ThreadFactory

+{

+	private static final AtomicInteger POOL_SEQ = new AtomicInteger(1);

+

+	private final AtomicInteger mThreadNum = new AtomicInteger(1);

+

+	private final String mPrefix;

+

+	private final boolean mDaemo;

+

+	private final ThreadGroup mGroup;

+

+	public NamedThreadFactory()

+	{

+		this("pool-" + POOL_SEQ.getAndIncrement(),false);

+	}

+

+	public NamedThreadFactory(String prefix)

+	{

+		this(prefix,false);

+	}

+

+	public NamedThreadFactory(String prefix,boolean daemo)

+	{

+		mPrefix = prefix + "-thread-";

+		mDaemo = daemo;

+        SecurityManager s = System.getSecurityManager();

+        mGroup = ( s == null ) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();

+	}

+

+	public Thread newThread(Runnable runnable)

+	{

+		String name = mPrefix + mThreadNum.getAndIncrement();

+        Thread ret = new Thread(mGroup,runnable,name,0);

+        ret.setDaemon(mDaemo);

+        return ret;

+	}

+

+	public ThreadGroup getThreadGroup()

+	{

+		return mGroup;

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/NetUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/NetUtils.java
new file mode 100644
index 0000000..c227ace
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/NetUtils.java
@@ -0,0 +1,266 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;

+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Random;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * IP and Port Helper for RPC, 
+ * 
+ * @author shawn.qianx
+ */
+
+public class NetUtils {
+    
+    private static final Logger logger = LoggerFactory.getLogger(NetUtils.class);
+
+    public static final String LOCALHOST = "127.0.0.1";
+
+    public static final String ANYHOST = "0.0.0.0";
+
+    private static final int RND_PORT_START = 30000;
+    
+    private static final int RND_PORT_RANGE = 10000;
+    
+    private static final Random RANDOM = new Random(System.currentTimeMillis());
+    
+    public static int getRandomPort() {
+        return RND_PORT_START + RANDOM.nextInt(RND_PORT_RANGE);
+    }
+
+    public static int getAvailablePort() {
+        ServerSocket ss = null;
+        try {
+            ss = new ServerSocket();
+            ss.bind(null);
+            return ss.getLocalPort();
+        } catch (IOException e) {
+            return getRandomPort();
+        } finally {
+            if (ss != null) {
+                try {
+                    ss.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    private static final int MIN_PORT = 0;
+    
+    private static final int MAX_PORT = 65535;
+    
+    public static boolean isInvalidPort(int port){
+        return port > MIN_PORT || port <= MAX_PORT;
+    }
+
+    private static final Pattern ADDRESS_PATTERN = Pattern.compile("^\\d{1,3}(\\.\\d{1,3}){3}\\:\\d{1,5}$");
+
+    public static boolean isValidAddress(String address){
+    	return ADDRESS_PATTERN.matcher(address).matches();
+    }
+
+    private static final Pattern LOCAL_IP_PATTERN = Pattern.compile("127(\\.\\d{1,3}){3}$");
+    

+    public static boolean isLocalHost(String host) {

+        return host != null 

+                && (LOCAL_IP_PATTERN.matcher(host).matches() 

+                        || host.equalsIgnoreCase("localhost"));

+    }

+

+    public static boolean isAnyHost(String host) {

+        return "0.0.0.0".equals(host);

+    }

+    

+    public static boolean isInvalidLocalHost(String host) {
+        return host == null 
+        			|| host.length() == 0
+                    || host.equalsIgnoreCase("localhost")
+                    || host.equals("0.0.0.0")
+                    || (LOCAL_IP_PATTERN.matcher(host).matches());
+    }
+    
+    public static boolean isValidLocalHost(String host) {
+    	return ! isInvalidLocalHost(host);
+    }
+
+    public static InetSocketAddress getLocalSocketAddress(String host, int port) {
+        return isInvalidLocalHost(host) ? 
+        		new InetSocketAddress(port) : new InetSocketAddress(host, port);
+    }
+
+    private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");
+
+    private static boolean isValidAddress(InetAddress address) {
+        if (address == null || address.isLoopbackAddress())
+            return false;
+        String name = address.getHostAddress();
+        return (name != null 
+                && ! ANYHOST.equals(name)
+                && ! LOCALHOST.equals(name) 
+                && IP_PATTERN.matcher(name).matches());
+    }
+    
+    public static String getLocalHost(){
+        InetAddress address = getLocalAddress();
+        return address == null ? LOCALHOST : address.getHostAddress();
+    }
+    
+    public static String filterLocalHost(String host) {
+    	if (NetUtils.isInvalidLocalHost(host)) {
+    		return NetUtils.getLocalHost();
+    	}
+    	return host;
+    }
+    
+    private static volatile InetAddress LOCAL_ADDRESS = null;
+
+    /**
+     * 遍历本地网卡,返回第一个合理的IP。
+     * 
+     * @return 本地网卡IP
+     */
+    public static InetAddress getLocalAddress() {
+        if (LOCAL_ADDRESS != null)

+            return LOCAL_ADDRESS;

+        InetAddress localAddress = getLocalAddress0();

+        LOCAL_ADDRESS = localAddress;

+        return localAddress;
+    }

+    

+    public static String getLogHost() {

+        InetAddress address = LOCAL_ADDRESS;

+        return address == null ? LOCALHOST : address.getHostAddress();

+    }
+    
+    private static InetAddress getLocalAddress0() {
+        InetAddress localAddress = null;
+        try {
+            localAddress = InetAddress.getLocalHost();
+            if (isValidAddress(localAddress)) {
+                return localAddress;
+            }
+        } catch (Throwable e) {

+            logger.warn("Failed to retriving ip address, " + e.getMessage(), e);

+        }
+        try {
+            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+            if (interfaces != null) {
+                while (interfaces.hasMoreElements()) {
+                    try {
+                        NetworkInterface network = interfaces.nextElement();
+                        Enumeration<InetAddress> addresses = network.getInetAddresses();
+                        if (addresses != null) {
+                            while (addresses.hasMoreElements()) {
+                                try {
+                                    InetAddress address = addresses.nextElement();
+                                    if (isValidAddress(address)) {
+                                        return address;
+                                    }
+                                } catch (Throwable e) {
+                                    logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
+                                }
+                            }
+                        }
+                    } catch (Throwable e) {
+                        logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
+        }
+        logger.error("Could not get local host ip address, will use 127.0.0.1 instead.");
+        return localAddress;
+    }
+    
+    private static final Map<String, String> hostNameCache = new LRUCache<String, String>(1000);
+
+    public static String getHostName(String address) {
+    	try {
+    		int i = address.indexOf(':');
+    		if (i > -1) {
+    			address = address.substring(0, i);
+    		}
+    		String hostname = hostNameCache.get(address);
+    		if (hostname != null && hostname.length() > 0) {
+    			return hostname;
+    		}
+    		InetAddress inetAddress = InetAddress.getByName(address);
+    		if (inetAddress != null) {
+    			hostname = inetAddress.getHostName();
+    			hostNameCache.put(address, hostname);
+    			return hostname;
+    		}
+		} catch (Throwable e) {
+			// ignore
+		}
+		return address;
+    }

+    

+    /**

+     * @param hostName

+     * @return ip address or hostName if UnknownHostException 

+     */

+    public static String getIpByHost(String hostName) {

+        try{

+            return InetAddress.getByName(hostName).getHostAddress();

+        }catch (UnknownHostException e) {

+            return hostName;

+        }

+    }

+

+    public static String toAddressString(InetSocketAddress address) {
+        return address.getAddress().getHostAddress() + ":" + address.getPort();
+    }
+    
+    public static InetSocketAddress toAddress(String address) {
+        int i = address.indexOf(':');
+        String host;
+        int port;
+        if (i > -1) {
+            host = address.substring(0, i);
+            port = Integer.parseInt(address.substring(i + 1));
+        } else {
+            host = address;
+            port = 0;
+        }
+        return new InetSocketAddress(host, port);
+    }
+    
+    public static String toURL(String protocol, String host, int port, String path) {
+		StringBuilder sb = new StringBuilder();
+		sb.append(protocol).append("://");
+		sb.append(host).append(':').append(port);
+		if( path.charAt(0) != '/' )
+			sb.append('/');
+		sb.append(path);
+		return sb.toString();
+	}
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/PojoUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/PojoUtils.java
new file mode 100644
index 0000000..4be5446
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/PojoUtils.java
@@ -0,0 +1,469 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.lang.reflect.Array;

+import java.lang.reflect.Constructor;

+import java.lang.reflect.Field;

+import java.lang.reflect.InvocationHandler;

+import java.lang.reflect.InvocationTargetException;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.lang.reflect.ParameterizedType;

+import java.lang.reflect.Proxy;

+import java.lang.reflect.Type;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+

+/**

+ * PojoUtils. Travel object deeply, and convert complex type to simple type.

+ * <p>

+ * Type below will be remained: 

+ * <ul>

+ * <li> Primitive Type, also include <b>String</b>, <b>Number</b>(Integer, Long), <b>Date</b>

+ * <li> Array of Primitive Type

+ * <li> Collection, eg: List, Map, Set etc.

+ * </ul>

+ * <p>

+ * Other type will be covert to a map which contains the attributes and value pair of object.

+ * 

+ * @author william.liangf

+ * @author ding.lid

+ */

+public class PojoUtils {

+

+    public static Object[] generalize(Object[] objs) {

+        Object[] dests = new Object[objs.length];

+        for (int i = 0; i < objs.length; i ++) {

+            dests[i] = generalize(objs[i]);

+        }

+        return dests;

+    }

+

+    public static Object[] realize(Object[] objs, Class<?>[] types) {

+        if (objs.length != types.length)

+            throw new IllegalArgumentException("args.length != types.length");

+        Object[] dests = new Object[objs.length];

+        for (int i = 0; i < objs.length; i ++) {

+            dests[i] = realize(objs[i], types[i]);

+        }

+        return dests;

+    }

+

+    public static Object[] realize(Object[] objs, Class<?>[] types, Type[] gtypes) {

+        if (objs.length != types.length

+                || objs.length != gtypes.length)

+            throw new IllegalArgumentException("args.length != types.length");

+        Object[] dests = new Object[objs.length];

+        for (int i = 0; i < objs.length; i ++) {

+            dests[i] = realize(objs[i], types[i], gtypes[i]);

+        }

+        return dests;

+    }

+

+    public static Object generalize(Object pojo) {

+        return generalize(pojo, new HashMap<Integer, Object>());

+    }

+

+    @SuppressWarnings("unchecked")

+    private static Object generalize(Object pojo, Map<Integer, Object> history) {

+        if (pojo == null) {

+            return null;

+        }

+        

+        if (pojo instanceof Enum<?>) {

+            return ((Enum<?>)pojo).name();

+        }

+        if (pojo.getClass().isArray() 

+        		&& Enum.class.isAssignableFrom(

+        				pojo.getClass().getComponentType())) {

+        	int len = Array.getLength(pojo);

+        	String[] values = new String[len];

+        	for (int i = 0; i < len; i ++) {

+        		values[i] = ((Enum<?>)Array.get(pojo, i)).name();

+        	}

+            return values;

+        }

+        

+        if (ReflectUtils.isPrimitives(pojo.getClass())) {

+            return pojo;

+        }

+        

+        Integer id = System.identityHashCode(pojo);

+        if (history.containsKey(id)) {

+            return history.get(id);

+        }

+        history.put(id, pojo);

+        

+        if (pojo.getClass().isArray()) {

+            int len = Array.getLength(pojo);

+            Object[] dest = new Object[len];

+            for (int i = 0; i < len; i ++) {

+                Object obj = Array.get(pojo, i);

+                dest[i] = generalize(obj, history);

+            }

+            return dest;

+        }

+        if (pojo instanceof Collection<?>) {

+            Collection<Object> src = (Collection<Object>)pojo;

+            int len = src.size();

+            Collection<Object> dest = (pojo instanceof List<?>) ? new ArrayList<Object>(len) : new HashSet<Object>(len);

+            for (Object obj : src) {

+                dest.add(generalize(obj, history));

+            }

+            return dest;

+        }

+        if (pojo instanceof Map<?, ?>) {

+            Map<Object, Object> src = (Map<Object, Object>)pojo;

+            Map<Object, Object> tmp = new HashMap<Object, Object>(src.size());

+            tmp.putAll(src);

+            for (Map.Entry<Object, Object> obj : tmp.entrySet()) {

+            	src.put(generalize(obj.getKey(), history), generalize(obj.getValue(), history));

+            }

+            return src;

+        }

+        Map<String, Object> map = new HashMap<String, Object>();

+        history.put(id, map);

+        map.put("class", pojo.getClass().getName());

+        for (Method method : pojo.getClass().getMethods()) {

+            if (Modifier.isPublic(method.getModifiers())

+                    && method.getDeclaringClass() != Object.class

+                    && method.getParameterTypes().length == 0) {

+                String name = method.getName();

+                try {

+                    if (name.startsWith("get")) {

+                        map.put(name.substring(3, 4).toLowerCase() + name.substring(4), generalize(method

+                                .invoke(pojo, new Object[0]), history));

+                    } else if (name.startsWith("is")) {

+                        map.put(name.substring(2, 3).toLowerCase() + name.substring(3), generalize(method

+                                .invoke(pojo, new Object[0]), history));

+                    }

+                } catch (Exception e) {

+                    throw new RuntimeException(e.getMessage(), e);

+                }

+            }

+        }

+        return map;

+    }

+    

+    public static Object realize(Object pojo, Class<?> type) {

+        return realize(pojo, type, new HashMap<Integer, Object>());

+    }

+    

+    public static Object realize(Object pojo, Class<?> type, Type genericType) {

+        return realize(pojo, type, genericType, new HashMap<Integer, Object>());

+    }

+    

+    private static class PojoInvocationHandler implements InvocationHandler {

+        

+        private Map<Object, Object> map;

+

+        public PojoInvocationHandler(Map<Object, Object> map) {

+            this.map = map;

+        }

+

+        @SuppressWarnings("unchecked")

+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

+            if (method.getDeclaringClass() == Object.class) {

+                return method.invoke(map, args);

+            }

+            String methodName = method.getName();

+            Object value = null;

+            if (methodName.length() > 3 && methodName.startsWith("get")) {

+                value = map.get(methodName.substring(3, 4).toLowerCase() + methodName.substring(4));

+            } else if (methodName.length() > 2 && methodName.startsWith("is")) {

+                value = map.get(methodName.substring(2, 3).toLowerCase() + methodName.substring(3));

+            } else {

+                value = map.get(methodName.substring(0, 1).toLowerCase() + methodName.substring(1));

+            }

+            if (value instanceof Map<?,?> && ! Map.class.isAssignableFrom(method.getReturnType())) {

+                value = realize((Map<String, Object>)value, method.getReturnType(), new HashMap<Integer, Object>());

+            }

+            return value;

+        }

+    }

+    

+    @SuppressWarnings("unchecked")

+	private static Collection<Object> createCollection(Class<?> type, int len) {

+    	if (type.isAssignableFrom(ArrayList.class)) {

+    		return  new ArrayList<Object>(len);

+    	}

+    	if (type.isAssignableFrom(HashSet.class)) {

+    		return new HashSet<Object>(len);

+    	}

+    	if (! type.isInterface() && ! Modifier.isAbstract(type.getModifiers())) {

+    		try {

+				return (Collection<Object>) type.newInstance();

+			} catch (Exception e) {

+				// ignore

+			}

+    	}

+    	return new ArrayList<Object>();

+    }

+

+    private static Object realize(Object pojo, Class<?> type, final Map<Integer, Object> history) {

+        return realize(pojo, type, null , history);

+    }

+    

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    private static Object realize(Object pojo, Class<?> type, Type genericType, final Map<Integer, Object> history) {

+        if (pojo == null) {

+            return null;

+        }

+        

+        if (type != null && type.isEnum() 

+        		&& pojo.getClass() == String.class) {

+    		return Enum.valueOf((Class<Enum>)type, (String)pojo);

+    	}

+        

+        if (ReflectUtils.isPrimitives(pojo.getClass()) 

+        		&& ! (type != null && type.isArray() 

+        				&& type.getComponentType().isEnum()

+        				&& pojo.getClass() == String[].class)) {

+            return CompatibleTypeUtils.compatibleTypeConvert(pojo, type);

+        }

+        

+        Integer id = System.identityHashCode(pojo);

+        if (history.containsKey(id)) {

+            return history.get(id);

+        }

+        history.put(id, pojo);

+        

+        if (pojo.getClass().isArray()) {

+        	if (Collection.class.isAssignableFrom(type)) {

+        		Class<?> ctype = pojo.getClass().getComponentType();

+	            int len = Array.getLength(pojo);

+        		Collection dest = createCollection(type, len);

+        		for (int i = 0; i < len; i ++) {

+	                Object obj = Array.get(pojo, i);

+	                Object value = realize(obj, ctype, history);

+	                dest.add(value);

+	            }

+	            return dest;

+        	} else {

+	        	Class<?> ctype = (type != null && type.isArray() ? type.getComponentType() : pojo.getClass().getComponentType());

+	            int len = Array.getLength(pojo);

+	            Object dest = Array.newInstance(ctype, len);

+	            for (int i = 0; i < len; i ++) {

+	                Object obj = Array.get(pojo, i);

+	                Object value = realize(obj, ctype, history);

+	                Array.set(dest, i, value);

+	            }

+	            return dest;

+            }

+        }

+        

+        if (pojo instanceof Collection<?>) {

+        	if (type.isArray()) {

+        		Class<?> ctype = type.getComponentType();

+                Collection<Object> src = (Collection<Object>)pojo;

+                int len = src.size();

+                Object dest = Array.newInstance(ctype, len);

+                int i = 0;

+                for (Object obj : src) {

+                    Object value = realize(obj, ctype, history);

+                    Array.set(dest, i, value);

+                    i ++;

+                }

+                return dest;

+        	} else {

+        		Collection<Object> src = (Collection<Object>)pojo;

+                int len = src.size();

+                Collection<Object> dest = createCollection(type, len);

+                for (Object obj : src) {

+                    Type keyType = getGenericClassByIndex(genericType, 0);

+                    Class<?> keyClazz = obj.getClass() ;

+                    if ( keyType instanceof Class){

+                      keyClazz = (Class<?>)keyType;

+                    } 

+                	Object value = realize(obj, keyClazz, keyType, history);

+                    dest.add(value);

+                }

+                return dest;

+        	}

+        }

+        

+        if (pojo instanceof Map<?, ?> && type != null) {

+        	Object className = ((Map<Object, Object>)pojo).get("class");

+            if (className instanceof String && ! Map.class.isAssignableFrom(type)) {

+                try {

+                    type = ClassHelper.forName((String)className);

+                } catch (ClassNotFoundException e) {

+                    // ignore

+                }

+            }

+            Map<Object, Object> map ;

+            // 返回值类型不是方法签名类型的子集 并且 不是接口类型

+            if (! type.isInterface()

+                    && ! type.isAssignableFrom(pojo.getClass())){

+                try {

+                    map = (Map<Object,Object>)type.newInstance();

+                } catch (Exception e) {

+                    //ignore error

+                    map = (Map<Object, Object>)pojo;

+                }

+            }else {

+                map = (Map<Object, Object>)pojo;

+            }

+            

+            if (Map.class.isAssignableFrom(type) || type == Object.class) {

+            	final Map<Object, Object> tmp = new HashMap<Object, Object>(map.size());

+            	tmp.putAll(map);

+            	for (Map.Entry<Object, Object> entry : tmp.entrySet()) {

+            	    Type keyType = getGenericClassByIndex(genericType, 0);

+            	    Type valueType = getGenericClassByIndex(genericType, 1);

+            	    Class<?> keyClazz;

+            	    if ( keyType instanceof Class){

+            	        keyClazz = (Class<?>)keyType;

+            	    } else {

+            	        keyClazz = entry.getKey() == null ? null : entry.getKey().getClass();

+            	    }

+            	    Class<?> valueClazz;

+                    if ( valueType instanceof Class){

+                        valueClazz = (Class<?>)valueType;

+                    } else {

+                        valueClazz = entry.getValue() == null ? null : entry.getValue().getClass() ;

+                    }

+            	    

+            	    Object key = keyClazz == null ? entry.getKey() : realize(entry.getKey(), keyClazz, keyType, history);

+            	    Object value = valueClazz == null ? entry.getValue() : realize(entry.getValue(), valueClazz, valueType, history);

+            		map.put(key, value);

+            	}

+        		return map;

+        	} else if (type.isInterface()) {

+        	    Object dest = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{type}, new PojoInvocationHandler(map));

+                history.put(id, dest);

+                return dest;

+            } else {

+                Object dest = newInstance(type);

+                history.put(id, dest);

+                for (Map.Entry<Object, Object> entry : map.entrySet()) {

+                	Object key = entry.getKey();

+                	if (key instanceof String) {

+	                    String name = (String) key;

+	                    Object value = entry.getValue();

+	                    if (value != null) {

+	                        Method method = getSetterMethod(dest.getClass(), name, value.getClass());

+	                        if (method != null) {

+	                            if (! method.isAccessible())

+	                                method.setAccessible(true);

+	                            Type ptype = method.getGenericParameterTypes()[0];

+	                            value = realize(value, method.getParameterTypes()[0], ptype, history);

+	                            try {

+	                                method.invoke(dest, value);

+	                            } catch (Exception e) {

+	                                throw new RuntimeException("Failed to set pojo " + dest.getClass().getSimpleName() + " property " + name + " value " + value + ", cause: " + e.getMessage(), e);

+	                            }

+	                        }

+	                    }

+                	}

+                }

+                if (dest instanceof Throwable) {

+                    Object message = map.get("message");

+                    if (message instanceof String) {

+                        try {

+                            Field filed = Throwable.class.getDeclaredField("detailMessage");

+                            if(! filed.isAccessible()) {

+                                filed.setAccessible(true);

+                            }

+                            filed.set(dest, (String) message);

+                        } catch (Exception e) {

+                        }

+                    }

+                }

+                return dest;

+            }

+        }

+        return pojo;

+    }

+    

+    /**

+     * 获取范型的类型 

+     * @param genericType

+     * @param index

+     * @return List<Person>  返回Person.class ,Map<String,Person> index=0 返回String.class index=1 返回Person.class

+     */

+    private static Type getGenericClassByIndex(Type genericType, int index){

+        Type clazz = null ;

+        //范型参数转换 

+        if (genericType instanceof ParameterizedType){

+            ParameterizedType t = (ParameterizedType)genericType;

+            Type[] types = t.getActualTypeArguments();

+            clazz = types[index];

+        }

+        return clazz;

+    }

+    

+    private static Object newInstance(Class<?> cls) {

+        try {

+            return cls.newInstance();

+        } catch (Throwable t) {

+            try {

+                Constructor<?>[] constructors = cls.getConstructors();

+                if (constructors != null && constructors.length > 0) {

+                    throw new RuntimeException("Illegal constructor: " + cls.getName());

+                }

+                Constructor<?> constructor = constructors[0];

+                if (constructor.getParameterTypes().length > 0) {

+                    for (Constructor<?> c : constructors) {

+                        if (c.getParameterTypes().length < 

+                                constructor.getParameterTypes().length) {

+                            constructor = c;

+                            if (constructor.getParameterTypes().length == 0) {

+                                break;

+                            }

+                        }

+                    }

+                }

+                return constructor.newInstance(new Object[constructor.getParameterTypes().length]);

+            } catch (InstantiationException e) {

+                throw new RuntimeException(e.getMessage(), e);

+            } catch (IllegalAccessException e) {

+                throw new RuntimeException(e.getMessage(), e);

+            } catch (InvocationTargetException e) {

+                throw new RuntimeException(e.getMessage(), e);

+            }

+        }

+    }

+

+    private static Method getSetterMethod(Class<?> cls, String property, Class<?> valueCls) {

+        String name = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);

+        try {

+            return cls.getMethod(name, valueCls);

+        } catch (NoSuchMethodException e) {

+            for (Method method : cls.getMethods()) {

+                if (Modifier.isPublic(method.getModifiers())

+                        && method.getDeclaringClass() != Object.class

+                        && method.getParameterTypes().length == 1 

+                        && method.getName().equals(name)) {

+                    return method;

+                }

+            }

+        }

+        return null;

+    }

+    

+    public static boolean isPojo(Class<?> cls) {

+        return ! ReflectUtils.isPrimitives(cls)

+                && ! Collection.class.isAssignableFrom(cls) 

+                && ! Map.class.isAssignableFrom(cls);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Pool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Pool.java
new file mode 100644
index 0000000..01dd5a8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Pool.java
@@ -0,0 +1,124 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+/**
+ * Pool.
+ * 
+ * @author qian.lei
+ */
+
+public interface Pool<T>
+{
+	/**
+	 * clear.
+	 */
+	void clear() throws Exception;
+
+	/**
+	 * close pool.
+	 * 
+	 * @throws Exception.
+	 */
+	void close() throws Exception;
+
+	/**
+	 * borrow.
+	 * 
+	 * @return object.
+	 */
+	T borrowObject() throws Exception;
+
+	/**
+	 * borrow.
+	 * 
+	 * @param timeout timeout.
+	 * @return object.
+	 */
+	T borrowObject(long timeout) throws Exception;
+
+	/**
+	 * return object.
+	 * 
+	 * @param obj object.
+	 */
+	void returnObject(T obj) throws Exception;
+
+	/**
+	 * get factory.
+	 * 
+	 * @return Factory instance.
+	 */
+	Factory<T> getFactory();
+
+	/**
+	 * get idle number.
+	 * 
+	 * @return idle number.
+	 */
+	int getIdleNum();
+
+	/**
+	 * get active number.
+	 * 
+	 * @return active number.
+	 */
+	int getActiveNum();
+
+	/**
+	 * pool factory.
+	 */
+	public interface Factory<T>
+	{
+		/**
+		 * make object.
+		 * 
+		 * @return object.
+		 * @throws Exception.
+		 */
+		T makeObject() throws Exception;
+
+		/**
+		 * destroy object.
+		 * 
+		 * @param obj object.
+		 * @throws Exception.
+		 */
+		void destroyObject(T obj) throws Exception;
+
+		/**
+		 * activate object.
+		 * 
+		 * @param obj object.
+		 */
+		void activateObject(T obj);
+
+		/**
+		 * passivate object.
+		 * 
+		 * @param obj object.
+		 */
+		void passivateObject(T obj);
+
+//		/**
+//		 * validate object.
+//		 * 
+//		 * @param obj object.
+//		 * @return valid or not.
+//		 */
+//		boolean validateObject(T obj);
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Reference.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Reference.java
new file mode 100644
index 0000000..6e4c7fd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Reference.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+/**

+ * TODO Comment of Reference

+ * 

+ * @author william.liangf

+ */

+public class Reference<T> {

+    

+    private T value;

+    

+    public void set(T value) {

+        this.value = value;

+    }

+    

+    public T get() {

+        return value;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java
new file mode 100644
index 0000000..dc44cf9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java
@@ -0,0 +1,862 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.lang.reflect.Array;

+import java.lang.reflect.Constructor;

+import java.lang.reflect.GenericArrayType;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.lang.reflect.ParameterizedType;

+import java.net.URL;

+import java.security.CodeSource;

+import java.security.ProtectionDomain;

+import java.util.ArrayList;

+import java.util.Date;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+import javassist.CtClass;

+import javassist.CtConstructor;

+import javassist.CtMethod;

+import javassist.NotFoundException;

+

+/**

+ * ReflectUtils

+ * 

+ * @author qian.lei

+ */

+public final class ReflectUtils {

+    

+	/**

+	 * void(V).

+	 */

+	public static final char JVM_VOID = 'V';

+

+	/**

+	 * boolean(Z).

+	 */

+	public static final char JVM_BOOLEAN = 'Z';

+

+	/**

+	 * byte(B).

+	 */

+	public static final char JVM_BYTE = 'B';

+

+	/**

+	 * char(C).

+	 */

+	public static final char JVM_CHAR = 'C';

+

+	/**

+	 * double(D).

+	 */

+	public static final char JVM_DOUBLE = 'D';

+

+	/**

+	 * float(F).

+	 */

+	public static final char JVM_FLOAT = 'F';

+

+	/**

+	 * int(I).

+	 */

+	public static final char JVM_INT = 'I';

+

+	/**

+	 * long(J).

+	 */

+	public static final char JVM_LONG = 'J';

+

+	/**

+	 * short(S).

+	 */

+	public static final char JVM_SHORT = 'S';

+

+	public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];

+

+	public static final String JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)";

+

+	public static final String JAVA_NAME_REGEX = "(?:" + JAVA_IDENT_REGEX + "(?:\\." + JAVA_IDENT_REGEX + ")*)";

+

+	public static final String CLASS_DESC = "(?:L" + JAVA_IDENT_REGEX   + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)";

+

+	public static final String ARRAY_DESC  = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))";

+

+	public static final String DESC_REGEX = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")";

+

+	public static final Pattern DESC_PATTERN = Pattern.compile(DESC_REGEX);

+

+	public static final String METHOD_DESC_REGEX = "(?:("+JAVA_IDENT_REGEX+")?\\(("+DESC_REGEX+"*)\\)("+DESC_REGEX+")?)";

+

+	public static final Pattern METHOD_DESC_PATTERN = Pattern.compile(METHOD_DESC_REGEX);

+

+	public static final Pattern GETTER_METHOD_DESC_PATTERN = Pattern.compile("get([A-Z][_a-zA-Z0-9]*)\\(\\)(" + DESC_REGEX + ")");

+

+	public static final Pattern SETTER_METHOD_DESC_PATTERN = Pattern.compile("set([A-Z][_a-zA-Z0-9]*)\\((" + DESC_REGEX + ")\\)V");

+

+	public static final Pattern IS_HAS_CAN_METHOD_DESC_PATTERN = Pattern.compile("(?:is|has|can)([A-Z][_a-zA-Z0-9]*)\\(\\)Z");

+	

+	private static final ConcurrentMap<String, Class<?>>  DESC_CLASS_CACHE = new ConcurrentHashMap<String, Class<?>>();

+    

+	public static boolean isPrimitives(Class<?> cls) {

+        if (cls.isArray()) {

+            return isPrimitive(cls.getComponentType());

+        }

+        return isPrimitive(cls);

+    }

+    

+	public static boolean isPrimitive(Class<?> cls) {

+        return cls.isPrimitive() || cls == String.class || cls == Boolean.class || cls == Character.class 

+                || Number.class.isAssignableFrom(cls) || Date.class.isAssignableFrom(cls);

+    }

+	

+	/**

+	 * is compatible.

+	 * 

+	 * @param c class.

+	 * @param o instance.

+	 * @return compatible or not.

+	 */

+	public static boolean isCompatible(Class<?> c, Object o)

+	{

+		boolean pt = c.isPrimitive();

+		if( o == null )

+			return !pt;

+

+		if( pt )

+		{

+			if( c == int.class )

+				c = Integer.class;

+			else if( c == boolean.class )

+				c = Boolean.class;

+			else  if( c == long.class )

+				c = Long.class;

+			else if( c == float.class )

+				c = Float.class;

+			else if( c == double.class )

+				c = Double.class;

+			else if( c == char.class )

+				c = Character.class;

+			else if( c == byte.class )

+				c = Byte.class;

+			else if( c == short.class )

+				c = Short.class;

+		}

+		if( c == o.getClass() )

+			return true;

+		return c.isInstance(o);

+	}

+

+	/**

+	 * is compatible.

+	 * 

+	 * @param cs class array.

+	 * @param os object array.

+	 * @return compatible or not.

+	 */

+	public static boolean isCompatible(Class<?>[] cs, Object[] os)

+	{

+		int len = cs.length;

+		if( len != os.length ) return false;

+		if( len == 0 ) return true;

+		for(int i=0;i<len;i++)

+			if( !isCompatible(cs[i], os[i]) ) return false;

+		return true;

+	}

+	

+	public static String getCodeBase(Class<?> cls) {

+	    if (cls == null)

+	        return null;

+	    ProtectionDomain domain = cls.getProtectionDomain();

+	    if (domain == null)

+	        return null;

+	    CodeSource source = domain.getCodeSource();

+	    if (source == null)

+	        return null;

+	    URL location = source.getLocation();

+	    if (location == null)

+            return null;

+	    return location.getFile();

+	}

+

+	/**

+	 * get name.

+	 * java.lang.Object[][].class => "java.lang.Object[][]"

+	 * 

+	 * @param c class.

+	 * @return name.

+	 */

+	public static String getName(Class<?> c)

+	{

+		if( c.isArray() )

+		{

+			StringBuilder sb = new StringBuilder();

+			do

+			{

+				sb.append("[]");

+				c = c.getComponentType();

+			}

+			while( c.isArray() );

+

+			return c.getName() + sb.toString();

+		}

+		return c.getName();

+	}

+	

+    

+    public static Class<?> getGenericClass(Class<?> cls) {

+        return getGenericClass(cls, 0);

+    }

+

+    public static Class<?> getGenericClass(Class<?> cls, int i) {

+        try {

+            ParameterizedType parameterizedType = ((ParameterizedType) cls.getGenericInterfaces()[0]);

+            Object genericClass = parameterizedType.getActualTypeArguments()[i];

+            if (genericClass instanceof ParameterizedType) { // 处理多级泛型

+                return (Class<?>) ((ParameterizedType) genericClass).getRawType();

+            } else if (genericClass instanceof GenericArrayType) { // 处理数组泛型

+                return (Class<?>) ((GenericArrayType) genericClass).getGenericComponentType();

+            } else {

+                return (Class<?>) genericClass;

+            }

+        } catch (Throwable e) {

+            throw new IllegalArgumentException(cls.getName()

+                    + " generic type undefined!", e);

+        }

+    }

+

+

+	/**

+	 * get method name.

+	 * "void do(int)", "void do()", "int do(java.lang.String,boolean)"

+	 * 

+	 * @param m method.

+	 * @return name.

+	 */

+	public static String getName(final Method m)

+	{

+		StringBuilder ret = new StringBuilder();

+		ret.append(getName(m.getReturnType())).append(' ');

+		ret.append(m.getName()).append('(');

+		Class<?>[] parameterTypes = m.getParameterTypes();

+		for(int i=0;i<parameterTypes.length;i++)

+		{

+			if( i > 0 )

+				ret.append(',');

+			ret.append(getName(parameterTypes[i]));

+		}

+		ret.append(')');

+		return ret.toString();

+	}

+	

+	public static String getSignature(String methodName, Class<?>[] parameterTypes) {

+		StringBuilder sb = new StringBuilder(methodName);

+		sb.append("(");

+		if (parameterTypes != null && parameterTypes.length > 0) {

+			boolean first = true;

+			for (Class<?> type : parameterTypes) {

+				if (first) {

+					first = false;

+				} else {

+					sb.append(",");

+				}

+				sb.append(type.getName());

+			}

+		}

+		sb.append(")");

+		return sb.toString();

+	}

+

+	/**

+	 * get constructor name.

+	 * "()", "(java.lang.String,int)"

+	 * 

+	 * @param c constructor.

+	 * @return name.

+	 */

+	public static String getName(final Constructor<?> c)

+	{

+		StringBuilder ret = new StringBuilder("(");

+		Class<?>[] parameterTypes = c.getParameterTypes();

+		for(int i=0;i<parameterTypes.length;i++)

+		{

+			if( i > 0 )

+				ret.append(',');

+			ret.append(getName(parameterTypes[i]));

+		}

+		ret.append(')');

+		return ret.toString();

+	}

+

+	/**

+	 * get class desc.

+	 * boolean[].class => "[Z"

+	 * Object.class => "Ljava/lang/Object;"

+	 * 

+	 * @param c class.

+	 * @return desc.

+	 * @throws NotFoundException 

+	 */

+	public static String getDesc(Class<?> c)

+	{

+		StringBuilder ret = new StringBuilder();

+

+		while( c.isArray() )

+		{

+			ret.append('[');

+			c = c.getComponentType();

+		}

+

+		if( c.isPrimitive() )

+		{

+			String t = c.getName();

+			if( "void".equals(t) ) ret.append(JVM_VOID);

+			else if( "boolean".equals(t) ) ret.append(JVM_BOOLEAN);

+			else if( "byte".equals(t) ) ret.append(JVM_BYTE);

+			else if( "char".equals(t) ) ret.append(JVM_CHAR);

+			else if( "double".equals(t) ) ret.append(JVM_DOUBLE);

+			else if( "float".equals(t) ) ret.append(JVM_FLOAT);

+			else if( "int".equals(t) ) ret.append(JVM_INT);

+			else if( "long".equals(t) ) ret.append(JVM_LONG);

+			else if( "short".equals(t) ) ret.append(JVM_SHORT);

+		}

+		else

+		{

+			ret.append('L');

+			ret.append(c.getName().replace('.', '/'));

+			ret.append(';');

+		}

+		return ret.toString();

+	}

+

+	/**

+	 * get class array desc.

+	 * [int.class, boolean[].class, Object.class] => "I[ZLjava/lang/Object;"

+	 * 

+	 * @param cs class array.

+	 * @return desc.

+	 * @throws NotFoundException 

+	 */

+	public static String getDesc(final Class<?>[] cs)

+	{

+		if( cs.length == 0 )

+			return "";

+

+		StringBuilder sb = new StringBuilder(64);

+		for( Class<?> c : cs )

+			sb.append(getDesc(c));

+		return sb.toString();

+	}

+

+	/**

+	 * get method desc.

+	 * int do(int arg1) => "do(I)I"

+	 * void do(String arg1,boolean arg2) => "do(Ljava/lang/String;Z)V"

+	 * 

+	 * @param m method.

+	 * @return desc.

+	 */

+	public static String getDesc(final Method m)

+	{

+		StringBuilder ret = new StringBuilder(m.getName()).append('(');

+		Class<?>[] parameterTypes = m.getParameterTypes();

+		for(int i=0;i<parameterTypes.length;i++)

+			ret.append(getDesc(parameterTypes[i]));

+		ret.append(')').append(getDesc(m.getReturnType()));

+		return ret.toString();

+	}

+

+	/**

+	 * get constructor desc.

+	 * "()V", "(Ljava/lang/String;I)V"

+	 * 

+	 * @param c constructor.

+	 * @return desc

+	 */

+	public static String getDesc(final Constructor<?> c)

+	{

+		StringBuilder ret = new StringBuilder("(");

+		Class<?>[] parameterTypes = c.getParameterTypes();

+		for(int i=0;i<parameterTypes.length;i++)

+			ret.append(getDesc(parameterTypes[i]));

+		ret.append(')').append('V');

+		return ret.toString();

+	}

+

+	/**

+	 * get method desc.

+	 * "(I)I", "()V", "(Ljava/lang/String;Z)V"

+	 * 

+	 * @param m method.

+	 * @return desc.

+	 */

+	public static String getDescWithoutMethodName(Method m)

+	{

+		StringBuilder ret = new StringBuilder();

+		ret.append('(');

+		Class<?>[] parameterTypes = m.getParameterTypes();

+		for(int i=0;i<parameterTypes.length;i++)

+			ret.append(getDesc(parameterTypes[i]));

+		ret.append(')').append(getDesc(m.getReturnType()));

+		return ret.toString();

+	}

+

+	/**

+	 * get class desc.

+	 * Object.class => "Ljava/lang/Object;"

+	 * boolean[].class => "[Z"

+	 * 

+	 * @param c class.

+	 * @return desc.

+	 * @throws NotFoundException 

+	 */

+	public static String getDesc(final CtClass c) throws NotFoundException

+	{

+		StringBuilder ret = new StringBuilder();

+		if( c.isArray() )

+		{

+			ret.append('[');

+			ret.append(getDesc(c.getComponentType()));

+		}

+		else if( c.isPrimitive() )

+		{

+			String t = c.getName();

+			if( "void".equals(t) ) ret.append(JVM_VOID);

+			else if( "boolean".equals(t) ) ret.append(JVM_BOOLEAN);

+			else if( "byte".equals(t) ) ret.append(JVM_BYTE);

+			else if( "char".equals(t) ) ret.append(JVM_CHAR);

+			else if( "double".equals(t) ) ret.append(JVM_DOUBLE);

+			else if( "float".equals(t) ) ret.append(JVM_FLOAT);

+			else if( "int".equals(t) ) ret.append(JVM_INT);

+			else if( "long".equals(t) ) ret.append(JVM_LONG);

+			else if( "short".equals(t) ) ret.append(JVM_SHORT);

+		}

+		else

+		{

+			ret.append('L');

+			ret.append(c.getName().replace('.','/'));

+			ret.append(';');

+		}

+		return ret.toString();

+	}

+

+	/**

+	 * get method desc.

+	 * "do(I)I", "do()V", "do(Ljava/lang/String;Z)V"

+	 * 

+	 * @param m method.

+	 * @return desc.

+	 */

+	public static String getDesc(final CtMethod m) throws NotFoundException

+	{

+		StringBuilder ret = new StringBuilder(m.getName()).append('(');

+		CtClass[] parameterTypes = m.getParameterTypes();

+		for(int i=0;i<parameterTypes.length;i++)

+			ret.append(getDesc(parameterTypes[i]));

+		ret.append(')').append(getDesc(m.getReturnType()));

+		return ret.toString();

+	}

+

+	/**

+	 * get constructor desc.

+	 * "()V", "(Ljava/lang/String;I)V"

+	 * 

+	 * @param c constructor.

+	 * @return desc

+	 */

+	public static String getDesc(final CtConstructor c) throws NotFoundException

+	{

+		StringBuilder ret = new StringBuilder("(");

+		CtClass[] parameterTypes = c.getParameterTypes();

+		for(int i=0;i<parameterTypes.length;i++)

+			ret.append(getDesc(parameterTypes[i]));

+		ret.append(')').append('V');

+		return ret.toString();

+	}

+

+	/**

+	 * get method desc.

+	 * "(I)I", "()V", "(Ljava/lang/String;Z)V".

+	 * 

+	 * @param m method.

+	 * @return desc.

+	 */

+	public static String getDescWithoutMethodName(final CtMethod m) throws NotFoundException

+	{

+		StringBuilder ret = new StringBuilder();

+		ret.append('(');

+		CtClass[] parameterTypes = m.getParameterTypes();

+		for(int i=0;i<parameterTypes.length;i++)

+			ret.append(getDesc(parameterTypes[i]));

+		ret.append(')').append(getDesc(m.getReturnType()));

+		return ret.toString();

+	}

+

+	/**

+	 * name to desc.

+	 * java.util.Map[][] => "[[Ljava/util/Map;"

+	 * 

+	 * @param name name.

+	 * @return desc.

+	 */

+	public static String name2desc(String name)

+	{

+		StringBuilder sb = new StringBuilder();

+		int c = 0,index = name.indexOf('[');

+		if( index > 0 )

+		{

+			c = ( name.length() - index ) / 2;

+			name = name.substring(0,index);

+		}

+		while( c-- > 0 ) sb.append("[");

+		if( "void".equals(name) ) sb.append(JVM_VOID);

+		else if( "boolean".equals(name) ) sb.append(JVM_BOOLEAN);

+		else if( "byte".equals(name) ) sb.append(JVM_BYTE);

+		else if( "char".equals(name) ) sb.append(JVM_CHAR);

+		else if( "double".equals(name) ) sb.append(JVM_DOUBLE);

+		else if( "float".equals(name) ) sb.append(JVM_FLOAT);

+		else if( "int".equals(name) ) sb.append(JVM_INT);

+		else if( "long".equals(name) ) sb.append(JVM_LONG);

+		else if( "short".equals(name) ) sb.append(JVM_SHORT);

+		else sb.append('L').append(name.replace('.', '/')).append(';');

+		return sb.toString();

+	}

+

+	/**

+	 * desc to name.

+	 * "[[I" => "int[][]"

+	 * 

+	 * @param desc desc.

+	 * @return name.

+	 */

+	public static String desc2name(String desc)

+	{

+		StringBuilder sb = new StringBuilder();

+		int c = desc.lastIndexOf('[') + 1;

+		if( desc.length() == c+1 )

+		{

+			switch( desc.charAt(c) )

+			{

+				case JVM_VOID: { sb.append("void"); break; }

+				case JVM_BOOLEAN: { sb.append("boolean"); break; }

+				case JVM_BYTE: { sb.append("byte"); break; }

+				case JVM_CHAR: { sb.append("char"); break; }

+				case JVM_DOUBLE: { sb.append("double"); break; }

+				case JVM_FLOAT: { sb.append("float"); break; }

+				case JVM_INT: { sb.append("int"); break; }

+				case JVM_LONG: { sb.append("long"); break; }

+				case JVM_SHORT: { sb.append("short"); break; }

+				default:

+					throw new RuntimeException();

+			}

+		}

+		else

+		{

+			sb.append(desc.substring(c+1, desc.length()-1).replace('/','.'));

+		}

+		while( c-- > 0 ) sb.append("[]");

+		return sb.toString();

+	}

+	

+	public static Class<?> forName(String name) {

+		try {

+			return name2class(name);

+		} catch (ClassNotFoundException e) {

+			throw new IllegalStateException("Not found class " + name + ", cause: " + e.getMessage(), e);

+		}

+	}

+

+	/**

+	 * name to class.

+	 * "boolean" => boolean.class

+	 * "java.util.Map[][]" => java.util.Map[][].class

+	 * 

+	 * @param name name.

+	 * @return Class instance.

+	 */

+	public static Class<?> name2class(String name) throws ClassNotFoundException

+	{

+		return name2class(ClassHelper.getClassLoader(), name);

+	}

+

+	/**

+	 * name to class.

+	 * "boolean" => boolean.class

+	 * "java.util.Map[][]" => java.util.Map[][].class

+	 * 

+	 * @param cl ClassLoader instance.

+	 * @param name name.

+	 * @return Class instance.

+	 */

+	private static Class<?> name2class(ClassLoader cl, String name) throws ClassNotFoundException

+	{

+		int c = 0, index = name.indexOf('[');

+		if( index > 0 )

+		{

+			c = ( name.length() - index ) / 2;

+			name = name.substring(0, index);

+		}

+		if( c > 0 )

+		{

+			StringBuilder sb = new StringBuilder();

+			while( c-- > 0 )

+				sb.append("[");

+

+			if( "void".equals(name) ) sb.append(JVM_VOID);

+			else if( "boolean".equals(name) ) sb.append(JVM_BOOLEAN);

+			else if( "byte".equals(name) ) sb.append(JVM_BYTE);

+			else if( "char".equals(name) ) sb.append(JVM_CHAR);

+			else if( "double".equals(name) ) sb.append(JVM_DOUBLE);

+			else if( "float".equals(name) ) sb.append(JVM_FLOAT);

+			else if( "int".equals(name) ) sb.append(JVM_INT);

+			else if( "long".equals(name) ) sb.append(JVM_LONG);

+			else if( "short".equals(name) ) sb.append(JVM_SHORT);

+			else sb.append('L').append(name).append(';'); // "java.lang.Object" ==> "Ljava.lang.Object;"

+			name = sb.toString();

+		}

+		else

+		{

+			if( "void".equals(name) ) return void.class;

+			else if( "boolean".equals(name) ) return boolean.class;

+			else if( "byte".equals(name) ) return byte.class;

+			else if( "char".equals(name) ) return char.class;

+			else if( "double".equals(name) ) return double.class;

+			else if( "float".equals(name) ) return float.class;

+			else if( "int".equals(name) ) return int.class;

+			else if( "long".equals(name) ) return long.class;

+			else if( "short".equals(name) ) return short.class;

+		}

+

+		if( cl == null )

+			cl = ClassHelper.getClassLoader();

+		return Class.forName(name, true, cl);

+	}

+

+	/**

+	 * desc to class.

+	 * "[Z" => boolean[].class

+	 * "[[Ljava/util/Map;" => java.util.Map[][].class

+	 * 

+	 * @param desc desc.

+	 * @return Class instance.

+	 * @throws ClassNotFoundException 

+	 */

+	public static Class<?> desc2class(String desc) throws ClassNotFoundException

+	{

+		return desc2class(ClassHelper.getClassLoader(), desc);

+	}

+

+	/**

+	 * desc to class.

+	 * "[Z" => boolean[].class

+	 * "[[Ljava/util/Map;" => java.util.Map[][].class

+	 * 

+	 * @param cl ClassLoader instance.

+	 * @param desc desc.

+	 * @return Class instance.

+	 * @throws ClassNotFoundException 

+	 */

+	private static Class<?> desc2class(ClassLoader cl, String desc) throws ClassNotFoundException

+	{

+		switch( desc.charAt(0) )

+		{

+			case JVM_VOID: return void.class;

+			case JVM_BOOLEAN: return boolean.class;

+			case JVM_BYTE: return byte.class;

+			case JVM_CHAR: return char.class;

+			case JVM_DOUBLE: return double.class;

+			case JVM_FLOAT: return float.class;

+			case JVM_INT: return int.class;

+			case JVM_LONG: return long.class;

+			case JVM_SHORT: return short.class;

+			case 'L':

+				desc = desc.substring(1, desc.length()-1).replace('/', '.'); // "Ljava/lang/Object;" ==> "java.lang.Object"

+				break;

+			case '[':

+				desc = desc.replace('/', '.');  // "[[Ljava/lang/Object;" ==> "[[Ljava.lang.Object;"

+				break;

+			default:

+				throw new ClassNotFoundException("Class not found: " + desc);

+		}

+

+		if( cl == null )

+			cl = ClassHelper.getClassLoader();

+		Class<?> clazz = DESC_CLASS_CACHE.get(desc);

+		if(clazz==null){

+		    clazz = Class.forName(desc, true, cl);

+		    DESC_CLASS_CACHE.put(desc, clazz);

+		}

+		return clazz;

+	}

+

+	/**

+	 * get class array instance.

+	 * 

+	 * @param desc desc.

+	 * @return Class class array.

+	 * @throws ClassNotFoundException 

+	 */

+	public static Class<?>[] desc2classArray(String desc) throws ClassNotFoundException

+	{

+	    Class<?>[] ret = desc2classArray(ClassHelper.getClassLoader(), desc);

+		return ret;

+	}

+

+	/**

+	 * get class array instance.

+	 * 

+	 * @param cl ClassLoader instance.

+	 * @param desc desc.

+	 * @return Class[] class array.

+	 * @throws ClassNotFoundException 

+	 */

+	private static Class<?>[] desc2classArray(ClassLoader cl, String desc) throws ClassNotFoundException

+	{

+		if( desc.length() == 0 )

+			return EMPTY_CLASS_ARRAY;

+

+		List<Class<?>> cs = new ArrayList<Class<?>>();

+		Matcher m = DESC_PATTERN.matcher(desc);

+		while(m.find())

+			cs.add(desc2class(cl, m.group()));

+		return cs.toArray(EMPTY_CLASS_ARRAY);

+	}

+

+	/**

+	 * 根据方法签名从类中找出方法。

+	 * 

+	 * @param clazz 查找的类。

+	 * @param methodName 方法签名,形如method1(int, String)。也允许只给方法名不参数只有方法名,形如method2。

+	 * @return 返回查找到的方法。

+	 * @throws NoSuchMethodException

+	 * @throws ClassNotFoundException  

+	 * @throws IllegalStateException 给定的方法签名找到多个方法(方法签名中没有指定参数,又有有重载的方法的情况)

+	 */

+	public static Method findMethodByMethodSignature(Class<?> clazz, String methodName, String[] parameterTypes)

+	        throws NoSuchMethodException, ClassNotFoundException {

+        if (parameterTypes == null) {

+            List<Method> finded = new ArrayList<Method>();

+            for (Method m : clazz.getMethods()) {

+                if (m.getName().equals(methodName)) {

+                    finded.add(m);

+                }

+            }

+            if (finded.isEmpty()) {

+                throw new NoSuchMethodException("No such method " + methodName + " in class " + clazz);

+            }

+            if(finded.size() > 1) {

+                String msg = String.format("Not unique method for method name(%s) in class(%s), find %d methods.",

+                        methodName, clazz.getName(), finded.size());

+                throw new IllegalStateException(msg);

+            }

+            return finded.get(0);

+        } else {

+            Class<?>[] types = new Class<?>[parameterTypes.length];

+            for (int i = 0; i < parameterTypes.length; i ++) {

+                types[i] = ReflectUtils.name2class(parameterTypes[i]);

+            }

+            return clazz.getMethod(methodName, types);

+        }

+	}

+

+    public static Method findMethodByMethodName(Class<?> clazz, String methodName)

+    		throws NoSuchMethodException, ClassNotFoundException {

+    	return findMethodByMethodSignature(clazz, methodName, null);

+    }

+    

+    public static Constructor<?> findConstructor(Class<?> clazz, Class<?> paramType) throws NoSuchMethodException {

+    	Constructor<?> targetConstructor;

+		try {

+			targetConstructor = clazz.getConstructor(new Class<?>[] {paramType});

+		} catch (NoSuchMethodException e) {

+			targetConstructor = null;

+			Constructor<?>[] constructors = clazz.getConstructors();

+			for (Constructor<?> constructor : constructors) {

+				if (Modifier.isPublic(constructor.getModifiers()) 

+						&& constructor.getParameterTypes().length == 1

+						&& constructor.getParameterTypes()[0].isAssignableFrom(paramType)) {

+					targetConstructor = constructor;

+					break;

+				}

+			}

+			if (targetConstructor == null) {

+				throw e;

+			}

+		}

+		return targetConstructor;

+    }

+

+    /**

+     * 检查对象是否是指定接口的实现。

+     * <p>

+     * 不会触发到指定接口的{@link Class},所以如果ClassLoader中没有指定接口类时,也不会出错。

+     * 

+     * @param obj 要检查的对象

+     * @param interfaceClazzName 指定的接口名

+     * @return 返回{@code true},如果对象实现了指定接口;否则返回{@code false}。

+     */

+    public static boolean isInstance(Object obj, String interfaceClazzName) {

+        for (Class<?> clazz = obj.getClass(); 

+                clazz != null && !clazz.equals(Object.class); 

+                clazz = clazz.getSuperclass()) {

+            Class<?>[] interfaces = clazz.getInterfaces();

+            for (Class<?> itf : interfaces) {

+                if (itf.getName().equals(interfaceClazzName)) {

+                    return true;

+                }

+            }

+        }

+        return false;

+    }

+    

+    public static Object getEmptyObject(Class<?> returnType) {

+        Object value = null;

+        if (returnType == null) {

+            value = null;

+        } else if (returnType.isPrimitive()) {

+            value = null;

+        } else if (returnType.isArray()) {

+            value = Array.newInstance(returnType.getComponentType(), 0);

+        } else if (List.class.equals(returnType)) {

+            value = new ArrayList<Object>(0);

+        } else if (Set.class.equals(returnType)) {

+            value = new HashSet<Object>(0);

+        } else if (Map.class.equals(returnType)) {

+            value = new HashMap<Object, Object>(0);

+        } else if (String.class.equals(returnType)) {

+            value = "";

+        } else if (! returnType.isInterface()) {

+            try {

+                value = returnType.newInstance();

+            } catch (Exception e) {

+                value = null;

+            }

+        } else {

+            value = null;

+        }

+        return value;

+    }

+    

+	private ReflectUtils(){}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Stack.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Stack.java
new file mode 100644
index 0000000..ab4e121
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Stack.java
@@ -0,0 +1,147 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.util.ArrayList;

+import java.util.EmptyStackException;

+import java.util.List;

+

+/**

+ * Stack.

+ * 

+ * @author qian.lei

+ */

+

+public class Stack<E>

+{

+	private int mSize = 0;

+

+	private List<E> mElements = new ArrayList<E>();

+

+	public Stack(){}

+

+	/**

+	 * push.

+	 * 

+	 * @param ele

+	 */

+	public void push(E ele)

+	{

+		if( mElements.size() > mSize )

+			mElements.set(mSize, ele);

+		else

+			mElements.add(ele);

+		mSize++;

+	}

+

+	/**

+	 * pop.

+	 * 

+	 * @return the last element.

+	 */

+	public E pop()

+	{

+		if( mSize == 0 )

+			throw new EmptyStackException();

+		return mElements.set(--mSize, null);

+	}

+

+	/**

+	 * peek.

+	 * 

+	 * @return the last element.

+	 */

+	public E peek()

+	{

+		if( mSize == 0 )

+			throw new EmptyStackException();

+		return mElements.get(mSize-1);

+	}

+

+	/**

+	 * get.

+	 * 

+	 * @param index index.

+	 * @return element.

+	 */

+	public E get(int index)

+	{

+		if( index >= mSize )

+			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mSize);

+

+		return index < 0 ? mElements.get(index+mSize) : mElements.get(index);

+	}

+

+	/**

+	 * set.

+	 * 

+	 * @param index index.

+	 * @param value element.

+	 * @return old element.

+	 */

+	public E set(int index, E value)

+	{

+		if( index >= mSize )

+			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mSize);

+

+		return mElements.set(index < 0 ? index + mSize : index, value);

+	}

+

+	/**

+	 * remove.

+	 * 

+	 * @param index

+	 * @return element

+	 */

+	public E remove(int index)

+	{

+		if( index >= mSize )

+			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mSize);

+

+		E ret = mElements.remove(index < 0 ? index + mSize : index);

+		mSize--;

+		return ret;

+	}

+

+	/**

+	 * get stack size.

+	 * 

+	 * @return size.

+	 */

+	public int size()

+	{

+		return mSize;

+	}

+

+	/**

+	 * is empty.

+	 * 

+	 * @return empty or not.

+	 */

+	public boolean isEmpty()

+	{

+		return mSize == 0;

+	}

+

+	/**

+	 * clear stack.

+	 */

+	public void clear()

+	{

+		mSize = 0;

+		mElements.clear();

+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/StringUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/StringUtils.java
new file mode 100644
index 0000000..01186b9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/StringUtils.java
@@ -0,0 +1,456 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.TreeMap;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.io.UnsafeStringWriter;

+import com.alibaba.dubbo.common.json.JSON;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+

+/**

+ * StringUtils

+ * 

+ * @author qian.lei

+ */

+

+public final class StringUtils {

+

+    private static final Logger logger = LoggerFactory.getLogger(StringUtils.class);

+

+	public static final String[] EMPTY_STRING_ARRAY = new String[0];

+

+	private static final Pattern KVP_PATTERN = Pattern.compile("([_.a-zA-Z0-9][-_.a-zA-Z0-9]*)[=](.*)"); //key value pair pattern.

+	

+	private static final Pattern INT_PATTERN = Pattern.compile("^\\d+$");

+	

+	public static boolean isBlank(String str)

+	{

+		if( str == null || str.length() == 0 )

+			return true;

+		return false;

+	}

+

+	/**

+	 * is empty string.

+	 * 

+	 * @param str source string.

+	 * @return is empty.

+	 */

+	public static boolean isEmpty(String str)

+	{

+		if( str == null || str.length() == 0 )

+			return true;

+		return false;

+	}

+

+	/**

+	 * is not empty string.

+	 * 

+	 * @param str source string.

+	 * @return is not empty.

+	 */

+    public static boolean isNotEmpty(String str)

+    {

+        return str != null && str.length() > 0;

+    }

+    

+    /**

+     * 

+     * @param s1

+     * @param s2

+     * @return equals

+     */

+    public static boolean isEquals(String s1, String s2) {

+        if (s1 == null && s2 == null)

+            return true;

+        if (s1 == null || s2 == null)

+            return false;

+        return s1.equals(s2);

+    }

+    

+    /**

+     * is integer string.

+     * 

+     * @param str

+     * @return is integer

+     */

+    public static boolean isInteger(String str) {

+    	if (str == null || str.length() == 0)

+    		return false;

+        return INT_PATTERN.matcher(str).matches();

+    }

+    

+    public static int parseInteger(String str) {

+    	if (! isInteger(str))

+    		return 0;

+        return Integer.parseInt(str);

+    }

+

+    /**

+     * Returns true if s is a legal Java identifier.<p>

+     * <a href="http://www.exampledepot.com/egs/java.lang/IsJavaId.html">more info.</a>

+     */

+    public static boolean isJavaIdentifier(String s) {

+        if (s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) {

+            return false;

+        }

+        for (int i=1; i<s.length(); i++) {

+            if (!Character.isJavaIdentifierPart(s.charAt(i))) {

+                return false;

+            }

+        }

+        return true;

+    }

+    

+    public static boolean isContains(String values, String value) {

+        if (values == null || values.length() == 0) {

+            return false;

+        }

+        return isContains(Constants.COMMA_SPLIT_PATTERN.split(values), value);

+    }

+    

+    /**

+     * 

+     * @param values

+     * @param value

+     * @return contains

+     */

+    public static boolean isContains(String[] values, String value) {

+        if (value != null && value.length() > 0 && values != null && values.length > 0) {

+            for (String v : values) {

+                if (value.equals(v)) {

+                    return true;

+                }

+            }

+        }

+        return false;

+    }

+    

+	public static boolean isNumeric(String str) {

+		if (str == null) {

+			return false;

+		}

+		int sz = str.length();

+		for (int i = 0; i < sz; i++) {

+			if (Character.isDigit(str.charAt(i)) == false) {

+				return false;

+			}

+		}

+		return true;

+	}

+

+    /**

+     * 

+     * @param e

+     * @return string

+     */

+    public static String toString(Throwable e) {

+    	UnsafeStringWriter w = new UnsafeStringWriter();

+        PrintWriter p = new PrintWriter(w);

+        p.print(e.getClass().getName());

+        if (e.getMessage() != null) {

+            p.print(": " + e.getMessage());

+        }

+        p.println();

+        try {

+            e.printStackTrace(p);

+            return w.toString();

+        } finally {

+            p.close();

+        }

+    }

+    

+    /**

+     * 

+     * @param msg

+     * @param e

+     * @return string

+     */

+    public static String toString(String msg, Throwable e) {

+    	UnsafeStringWriter w = new UnsafeStringWriter();

+        w.write(msg + "\n");

+        PrintWriter p = new PrintWriter(w);

+        try {

+            e.printStackTrace(p);

+            return w.toString();

+        } finally {

+            p.close();

+        }

+    }

+

+	/**

+	 * translat.

+	 * 

+	 * @param src source string.

+	 * @param from src char table.

+	 * @param to target char table.

+	 * @return String.

+	 */

+	public static String translat(String src, String from, String to)

+	{

+		if( isEmpty(src) ) return src;

+		StringBuilder sb = null;

+		int ix;

+		char c;

+		for(int i=0,len=src.length();i<len;i++)

+		{

+			c = src.charAt(i);

+			ix = from.indexOf(c);

+			if( ix == -1 )

+			{

+				if( sb != null )

+					sb.append(c);

+			}

+			else

+			{

+				if( sb == null )

+				{

+					sb = new StringBuilder(len);

+					sb.append(src, 0, i);

+				}

+				if( ix < to.length() )

+					sb.append(to.charAt(ix));

+			}

+		}

+		return sb == null ? src : sb.toString();

+	}

+

+	/**

+	 * split.

+	 * 

+	 * @param ch char.

+	 * @return string array.

+	 */

+	public static String[] split(String str, char ch)

+	{

+		List<String> list = null;

+        char c;

+        int ix = 0,len=str.length();

+		for(int i=0;i<len;i++)

+		{

+			c = str.charAt(i);

+			if( c == ch )

+			{

+				if( list == null )

+					list = new ArrayList<String>();

+				list.add(str.substring(ix, i));

+				ix = i + 1;

+			}

+		}

+		if( ix > 0 )

+			list.add(str.substring(ix));

+		return list == null ? EMPTY_STRING_ARRAY : (String[])list.toArray(EMPTY_STRING_ARRAY);

+	}

+

+	/**

+	 * join string.

+	 * 

+	 * @param array String array.

+	 * @return String.

+	 */

+	public static String join(String[] array)

+	{

+		if( array.length == 0 ) return "";

+		StringBuilder sb = new StringBuilder();

+		for( String s : array )

+			sb.append(s);

+		return sb.toString();

+	}

+

+	/**

+	 * join string like javascript.

+	 * 

+	 * @param array String array.

+	 * @param split split

+	 * @return String.

+	 */

+	public static String join(String[] array, char split)

+	{

+		if( array.length == 0 ) return "";

+		StringBuilder sb = new StringBuilder();

+		for(int i=0;i<array.length;i++)

+		{

+			if( i > 0 )

+				sb.append(split);

+			sb.append(array[i]);

+		}

+		return sb.toString();

+	}

+

+	/**

+	 * join string like javascript.

+	 * 

+	 * @param array String array.

+	 * @param split split

+	 * @return String.

+	 */

+	public static String join(String[] array, String split)

+	{

+		if( array.length == 0 ) return "";

+		StringBuilder sb = new StringBuilder();

+		for(int i=0;i<array.length;i++)

+		{

+			if( i > 0 )

+				sb.append(split);

+			sb.append(array[i]);

+		}

+		return sb.toString();

+	}

+	

+	public static String join(Collection<String> coll, String split) {

+	    if(coll.isEmpty()) return "";

+	    

+	    StringBuilder sb = new StringBuilder();

+	    boolean isFirst = true;

+	    for(String s : coll) {

+	        if(isFirst) isFirst = false; else sb.append(split);

+	        sb.append(s);

+	    }

+	    return sb.toString();

+	}

+

+	/**

+	 * parse key-value pair.

+	 * 

+	 * @param str string.

+	 * @param itemSeparator item separator.

+	 * @return key-value map;

+	 */

+	private static Map<String, String> parseKeyValuePair(String str, String itemSeparator)

+	{

+		String[] tmp = str.split(itemSeparator);

+		Map<String, String> map = new HashMap<String, String>(tmp.length);

+		for(int i=0;i<tmp.length;i++)

+		{

+			Matcher matcher = KVP_PATTERN.matcher(tmp[i]);

+			if( matcher.matches() == false )

+				continue;

+			map.put(matcher.group(1), matcher.group(2));

+		}

+		return map;

+	}

+	

+	public static String getQueryStringValue(String qs, String key) {

+		Map<String, String> map = StringUtils.parseQueryString(qs);

+		return map.get(key);

+	}

+	

+	/**

+     * parse query string to Parameters.

+     * 

+     * @param qs query string.

+     * @return Parameters instance.

+     */

+	public static Map<String, String> parseQueryString(String qs)

+	{

+	    if( qs == null || qs.length() == 0 )

+            return new HashMap<String, String>();

+	    return parseKeyValuePair(qs, "\\&");

+	}

+	

+	public static String getServiceKey(Map<String, String> ps) {

+	    StringBuilder buf = new StringBuilder();

+        String group = ps.get(Constants.GROUP_KEY);

+        if (group != null && group.length()>0){

+            buf.append(group).append("/");

+        }

+        buf.append(ps.get(Constants.INTERFACE_KEY));

+        String version = ps.get(Constants.VERSION_KEY);

+        if (version!= null && version.length()>0){

+            buf.append(":").append(version);

+        }

+        return buf.toString();

+	}

+	

+	public static String toQueryString(Map<String, String> ps) {

+		StringBuilder buf = new StringBuilder();

+		if (ps != null && ps.size() > 0) {

+			for (Map.Entry<String, String> entry : new TreeMap<String, String>(ps).entrySet()) {

+				String key = entry.getKey();

+				String value = entry.getValue();

+				if (key != null && key.length() > 0

+						&& value != null && value.length() > 0) {

+					if (buf.length() > 0) {

+						buf.append("&");

+					}

+					buf.append(key);

+					buf.append("=");

+					buf.append(value);

+				}

+			}

+		}

+		return buf.toString();

+	}

+	

+	public static String camelToSplitName(String camelName, String split) {

+	    if (camelName == null || camelName.length() == 0) {

+	        return camelName;

+	    }

+	    StringBuilder buf = null;

+	    for (int i = 0; i < camelName.length(); i ++) {

+	        char ch = camelName.charAt(i);

+	        if (ch >= 'A' && ch <= 'Z') {

+	            if (buf == null) {

+	                buf = new StringBuilder();

+	                if (i > 0) {

+	                    buf.append(camelName.substring(0, i));

+	                }

+	            }

+	            if (i > 0) {

+	                buf.append(split);

+	            }

+	            buf.append(Character.toLowerCase(ch));

+	        } else if (buf != null) {

+	            buf.append(ch);

+	        }

+	    }

+	    return buf == null ? camelName : buf.toString();

+	}

+	

+	public static String toArgumentString(Object[] args) {

+	    StringBuilder buf = new StringBuilder();

+        for (Object arg : args) {

+            if (buf.length() > 0) {

+                buf.append(Constants.COMMA_SEPARATOR);

+            }

+            if (arg == null || ReflectUtils.isPrimitives(arg.getClass())) {

+                buf.append(arg);

+            } else {

+                try {

+                    buf.append(JSON.json(arg));

+                } catch (IOException e) {

+                    logger.warn(e.getMessage(), e);

+                    buf.append(arg);

+                }

+            }

+        }

+        return buf.toString();

+	}

+

+	private StringUtils(){}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java
new file mode 100644
index 0000000..7ce47e0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java
@@ -0,0 +1,335 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+

+public class UrlUtils {

+

+    public static URL parseURL(String address, Map<String, String> defaults) {

+        if (address == null || address.length() == 0) {

+            return null;

+        }

+        String url;

+        if (address.indexOf("://") >= 0) {

+            url = address;

+        } else {

+            String[] addresses = Constants.COMMA_SPLIT_PATTERN.split(address);

+            url = addresses[0];

+            if (addresses.length > 1) {

+                StringBuilder backup = new StringBuilder();

+                for (int i = 1; i < addresses.length; i++) {

+                    if (i > 1) {

+                        backup.append(",");

+                    }

+                    backup.append(NetUtils.filterLocalHost(addresses[i]));

+                }

+                url += "?" + Constants.BACKUP_KEY + "=" + backup.toString();

+            }

+        }

+        String defaultProtocol = defaults == null ? null : defaults.get("protocol");

+        if (defaultProtocol == null || defaultProtocol.length() == 0) {

+            defaultProtocol = "dubbo";

+        }

+        String defaultUsername = defaults == null ? null : defaults.get("username");

+        String defaultPassword = defaults == null ? null : defaults.get("password");

+        int defaultPort = StringUtils.parseInteger(defaults == null ? null : defaults.get("port"));

+        String defaultPath = defaults == null ? null : defaults.get("path");

+        Map<String, String> defaultParameters = defaults == null ? null : new HashMap<String, String>(defaults);

+        if (defaultParameters != null) {

+            defaultParameters.remove("protocol");

+            defaultParameters.remove("username");

+            defaultParameters.remove("password");

+            defaultParameters.remove("host");

+            defaultParameters.remove("port");

+            defaultParameters.remove("path");

+        }

+        URL u = URL.valueOf(url);

+        boolean changed = false;

+        String protocol = u.getProtocol();

+        String username = u.getUsername();

+        String password = u.getPassword();

+        String host = u.getHost();

+        int port = u.getPort();

+        String path = u.getPath();

+        Map<String, String> parameters = new HashMap<String, String>(u.getParameters());

+        if ((protocol == null || protocol.length() == 0) && defaultProtocol != null && defaultProtocol.length() > 0) {

+            changed = true;

+            protocol = defaultProtocol;

+        }

+        if ((username == null || username.length() == 0) && defaultUsername != null && defaultUsername.length() > 0) {

+            changed = true;

+            username = defaultUsername;

+        }

+        if ((password == null || password.length() == 0) && defaultPassword != null && defaultPassword.length() > 0) {

+            changed = true;

+            password = defaultPassword;

+        }

+        if (port <= 0) {

+            if (defaultPort > 0) {

+                changed = true;

+                port = defaultPort;

+            } else {

+                changed = true;

+                port = 9090;

+            }

+        }

+        if (path == null || path.length() == 0) {

+            if (defaultPath != null && defaultPath.length() > 0) {

+                changed = true;

+                path = defaultPath;

+            }

+        }

+        if (defaultParameters != null && defaultParameters.size() > 0) {

+            for (Map.Entry<String, String> entry : defaultParameters.entrySet()) {

+                String key = entry.getKey();

+                String defaultValue = entry.getValue();

+                if (defaultValue != null && defaultValue.length() > 0) {

+                    String value = parameters.get(key);

+                    if (value == null || value.length() == 0) {

+                        changed = true;

+                        parameters.put(key, defaultValue);

+                    }

+                }

+            }

+        }

+        if (changed) {

+            u = new URL(protocol, username, password, host, port, path, parameters);

+        }

+        return u;

+    }

+

+    public static List<URL> parseURLs(String address, Map<String, String> defaults) {

+        if (address == null || address.length() == 0) {

+            return null;

+        }

+        String[] addresses = Constants.REGISTRY_SPLIT_PATTERN.split(address);

+        if (addresses == null || addresses.length == 0) {

+            return null; //here won't be empty

+        }

+        List<URL> registries = new ArrayList<URL>();

+        for (String addr : addresses) {

+            registries.add(parseURL(addr, defaults));

+        }

+        return registries;

+    }

+

+    public static Map<String, Map<String, String>> convertRegister(Map<String, Map<String, String>> register) {

+        Map<String, Map<String, String>> newRegister = new HashMap<String, Map<String, String>>();

+        for (Map.Entry<String, Map<String, String>> entry : register.entrySet()) {

+            String serviceName = entry.getKey();

+            Map<String, String> serviceUrls = entry.getValue();

+            if (!serviceName.contains(":") && !serviceName.contains("/")) {

+                for (Map.Entry<String, String> entry2 : serviceUrls.entrySet()) {

+                    String serviceUrl = entry2.getKey();

+                    String serviceQuery = entry2.getValue();

+                    Map<String, String> params = StringUtils.parseQueryString(serviceQuery);

+                    String group = params.get("group");

+                    String version = params.get("version");

+                    params.remove("group");

+                    params.remove("version");

+                    String name = serviceName;

+                    if (group != null && group.length() > 0) {

+                        name = group + "/" + name;

+                    }

+                    if (version != null && version.length() > 0) {

+                        name = name + ":" + version;

+                    }

+                    Map<String, String> newUrls = newRegister.get(name);

+                    if (newUrls == null) {

+                        newUrls = new HashMap<String, String>();

+                        newRegister.put(name, newUrls);

+                    }

+                    newUrls.put(serviceUrl, StringUtils.toQueryString(params));

+                }

+            } else {

+                newRegister.put(serviceName, serviceUrls);

+            }

+        }

+        return newRegister;

+    }

+

+    public static Map<String, String> convertSubscribe(Map<String, String> subscribe) {

+        Map<String, String> newSubscribe = new HashMap<String, String>();

+        for (Map.Entry<String, String> entry : subscribe.entrySet()) {

+            String serviceName = entry.getKey();

+            String serviceQuery = entry.getValue();

+            if (!serviceName.contains(":") && !serviceName.contains("/")) {

+                Map<String, String> params = StringUtils.parseQueryString(serviceQuery);

+                String group = params.get("group");

+                String version = params.get("version");

+                params.remove("group");

+                params.remove("version");

+                String name = serviceName;

+                if (group != null && group.length() > 0) {

+                    name = group + "/" + name;

+                }

+                if (version != null && version.length() > 0) {

+                    name = name + ":" + version;

+                }

+                newSubscribe.put(name, StringUtils.toQueryString(params));

+            } else {

+                newSubscribe.put(serviceName, serviceQuery);

+            }

+        }

+        return newSubscribe;

+    }

+

+    public static Map<String, Map<String, String>> revertRegister(Map<String, Map<String, String>> register) {

+        Map<String, Map<String, String>> newRegister = new HashMap<String, Map<String, String>>();

+        for (Map.Entry<String, Map<String, String>> entry : register.entrySet()) {

+            String serviceName = entry.getKey();

+            Map<String, String> serviceUrls = entry.getValue();

+            if (serviceName.contains(":") || serviceName.contains("/")) {

+                for (Map.Entry<String, String> entry2 : serviceUrls.entrySet()) {

+                    String serviceUrl = entry2.getKey();

+                    String serviceQuery = entry2.getValue();

+                    Map<String, String> params = StringUtils.parseQueryString(serviceQuery);

+                    String name = serviceName;

+                    int i = name.indexOf('/');

+                    if (i >= 0) {

+                        params.put("group", name.substring(0, i));

+                        name = name.substring(i + 1);

+                    }

+                    i = name.lastIndexOf(':');

+                    if (i >= 0) {

+                        params.put("version", name.substring(i + 1));

+                        name = name.substring(0, i);

+                    }

+                    Map<String, String> newUrls = newRegister.get(name);

+                    if (newUrls == null) {

+                        newUrls = new HashMap<String, String>();

+                        newRegister.put(name, newUrls);

+                    }

+                    newUrls.put(serviceUrl, StringUtils.toQueryString(params));

+                }

+            } else {

+                newRegister.put(serviceName, serviceUrls);

+            }

+        }

+        return newRegister;

+    }

+

+    public static Map<String, String> revertSubscribe(Map<String, String> subscribe) {

+        Map<String, String> newSubscribe = new HashMap<String, String>();

+        for (Map.Entry<String, String> entry : subscribe.entrySet()) {

+            String serviceName = entry.getKey();

+            String serviceQuery = entry.getValue();

+            if (serviceName.contains(":") || serviceName.contains("/")) {

+                Map<String, String> params = StringUtils.parseQueryString(serviceQuery);

+                String name = serviceName;

+                int i = name.indexOf('/');

+                if (i >= 0) {

+                    params.put("group", name.substring(0, i));

+                    name = name.substring(i + 1);

+                }

+                i = name.lastIndexOf(':');

+                if (i >= 0) {

+                    params.put("version", name.substring(i + 1));

+                    name = name.substring(0, i);

+                }

+                newSubscribe.put(name, StringUtils.toQueryString(params));

+            } else {

+                newSubscribe.put(serviceName, serviceQuery);

+            }

+        }

+        return newSubscribe;

+    }

+

+    public static Map<String, Map<String, String>> revertNotify(Map<String, Map<String, String>> notify) {

+        if (notify != null && notify.size() > 0) {

+            Map<String, Map<String, String>> newNotify = new HashMap<String, Map<String, String>>();

+            for (Map.Entry<String, Map<String, String>> entry : notify.entrySet()) {

+                String serviceName = entry.getKey();

+                Map<String, String> serviceUrls = entry.getValue();

+                if (!serviceName.contains(":") && !serviceName.contains("/")) {

+                    if (serviceUrls != null && serviceUrls.size() > 0) {

+                        for (Map.Entry<String, String> entry2 : serviceUrls.entrySet()) {

+                            String url = entry2.getKey();

+                            String query = entry2.getValue();

+                            Map<String, String> params = StringUtils.parseQueryString(query);

+                            String group = params.get("group");

+                            String version = params.get("version");

+                            // params.remove("group");

+                            // params.remove("version");

+                            String name = serviceName;

+                            if (group != null && group.length() > 0) {

+                                name = group + "/" + name;

+                            }

+                            if (version != null && version.length() > 0) {

+                                name = name + ":" + version;

+                            }

+                            Map<String, String> newUrls = newNotify.get(name);

+                            if (newUrls == null) {

+                                newUrls = new HashMap<String, String>();

+                                newNotify.put(name, newUrls);

+                            }

+                            newUrls.put(url, StringUtils.toQueryString(params));

+                        }

+                    }

+                } else {

+                    newNotify.put(serviceName, serviceUrls);

+                }

+            }

+            return newNotify;

+        }

+        return notify;

+    }

+

+    //compatible for dubbo-2.0.0

+    public static List<String> revertForbid(List<String> forbid, Set<String> subscribed) {

+        if (forbid != null && forbid.size() > 0) {

+            List<String> newForbid = new ArrayList<String>();

+            for (String serviceName : forbid) {

+                if (!serviceName.contains(":") && !serviceName.contains("/")) {

+                    for (String name : subscribed) {

+                        if (name.contains(serviceName)) {

+                            newForbid.add(name);

+                            break;

+                        }

+                    }

+                } else {

+                    newForbid.add(serviceName);

+                }

+            }

+            return newForbid;

+        }

+        return forbid;

+    }

+

+    public static boolean isMatch(URL consumerUrl, URL providerUrl) {

+        String consumerInterface = consumerUrl.getServiceInterface();

+        String consumerGroup = consumerUrl.getParameter(Constants.GROUP_KEY);

+        String consumerVersion = consumerUrl.getParameter(Constants.VERSION_KEY);

+        String providerInterface = providerUrl.getServiceInterface();

+        String providerGroup = providerUrl.getParameter(Constants.GROUP_KEY);

+        String providerVersion = providerUrl.getParameter(Constants.VERSION_KEY);

+        return (Constants.ANY_VALUE.equals(consumerInterface) || StringUtils.isEquals(consumerInterface, providerInterface))

+               && (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))

+               && (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))

+               && (! Constants.SUBSCRIBE_PROTOCOL.equals(providerUrl.getProtocol()) || consumerUrl.getParameter(Constants.ADMIN_KEY, false));

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler
new file mode 100644
index 0000000..45d649e
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler
@@ -0,0 +1,3 @@
+adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler

+jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler

+javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory
new file mode 100644
index 0000000..21bf88f
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory
@@ -0,0 +1,2 @@
+adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory

+spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization
new file mode 100644
index 0000000..fec7fc4
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization
@@ -0,0 +1,6 @@
+dubbo=com.alibaba.dubbo.common.serialize.support.dubbo.DubboSerialization

+hessian2=com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization

+java=com.alibaba.dubbo.common.serialize.support.java.JavaSerialization

+compactedjava=com.alibaba.dubbo.common.serialize.support.java.CompactedJavaSerialization

+json=com.alibaba.dubbo.common.serialize.support.json.JsonSerialization

+fastjson=com.alibaba.dubbo.common.serialize.support.json.FastJsonSerialization
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..d4eca74
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+memory=com.alibaba.dubbo.common.status.support.MemoryStatusChecker

+load=com.alibaba.dubbo.common.status.support.LoadStatusChecker
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool
new file mode 100644
index 0000000..e698e73
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool
@@ -0,0 +1,2 @@
+fixed=com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool

+cached=com.alibaba.dubbo.common.threadpool.support.cached.CachedThreadPool
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/URLTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/URLTest.java
new file mode 100644
index 0000000..bff1227
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/URLTest.java
@@ -0,0 +1,552 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.*;

+

+import static org.hamcrest.CoreMatchers.*;

+

+import java.io.File;

+import java.util.Arrays;

+import java.util.HashMap;

+import java.util.Map;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.utils.CollectionUtils;

+

+/**

+ * @author ding.lid

+ * @author william.liangf

+ */

+public class URLTest {

+    

+    @Test

+    public void test_valueOf_noProtocolAndHost() throws Exception {

+        URL url = URL.valueOf("/context/path?version=1.0.0&application=morgan");

+        assertNull(url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertNull(url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+        

+

+        url = URL.valueOf("context/path?version=1.0.0&application=morgan");

+        //                 ^^^^^^^ Caution , parse as host

+        assertNull(url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertEquals("context", url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals("path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+    }

+	

+    @Test

+	public void test_valueOf_noProtocol() throws Exception {

+		URL url = URL.valueOf("10.20.130.230");

+		assertNull(url.getProtocol());

+		assertNull(url.getUsername());

+		assertNull(url.getPassword());

+		assertEquals("10.20.130.230", url.getHost());

+		assertEquals(0, url.getPort());

+		assertEquals(null, url.getPath());

+		assertEquals(0, url.getParameters().size());

+		

+		url = URL.valueOf("10.20.130.230:20880");

+        assertNull(url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals(null, url.getPath());

+        assertEquals(0, url.getParameters().size());

+        

+        url = URL.valueOf("10.20.130.230/context/path");

+        assertNull(url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(0, url.getParameters().size());

+        

+        url = URL.valueOf("10.20.130.230:20880/context/path");

+        assertNull(url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(0, url.getParameters().size());

+        

+        url = URL.valueOf("admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");

+        assertNull(url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+	}

+    

+    @Test

+    public void test_valueOf_noHost() throws Exception {

+        URL url = URL.valueOf("file:///home/user1/router.js");

+        assertEquals("file", url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertNull(url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals("home/user1/router.js", url.getPath());

+        assertEquals(0, url.getParameters().size());

+        

+        // Caution!! 

+        url = URL.valueOf("file://home/user1/router.js");

+        //                      ^^ only tow slash!

+        assertEquals("file", url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertEquals("home", url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals("user1/router.js", url.getPath());

+        assertEquals(0, url.getParameters().size());

+        

+

+        url = URL.valueOf("file:/home/user1/router.js");

+        assertEquals("file", url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertNull(url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals("home/user1/router.js", url.getPath());

+        assertEquals(0, url.getParameters().size());

+    

+        url = URL.valueOf("file:///d:/home/user1/router.js");

+        assertEquals("file", url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertNull(url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals("d:/home/user1/router.js", url.getPath());

+        assertEquals(0, url.getParameters().size());

+        

+        url = URL.valueOf("file:///home/user1/router.js?p1=v1&p2=v2");

+        assertEquals("file", url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertNull(url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals("home/user1/router.js", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        Map<String, String> params = new HashMap<String, String>();

+        params.put("p1", "v1");

+        params.put("p2", "v2");

+        assertEquals(params, url.getParameters());

+        

+        url = URL.valueOf("file:/home/user1/router.js?p1=v1&p2=v2");

+        assertEquals("file", url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertNull(url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals("home/user1/router.js", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        params = new HashMap<String, String>();

+        params.put("p1", "v1");

+        params.put("p2", "v2");

+        assertEquals(params, url.getParameters());

+    }

+    

+    @Test

+    public void test_valueOf_WithProtocolHost() throws Exception {

+        URL url = URL.valueOf("dubbo://10.20.130.230");

+        assertEquals("dubbo", url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(0, url.getPort());

+        assertEquals(null, url.getPath());

+        assertEquals(0, url.getParameters().size());

+        

+        url = URL.valueOf("dubbo://10.20.130.230:20880/context/path");

+        assertEquals("dubbo", url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(0, url.getParameters().size());

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880");

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals(null, url.getPath());

+        assertEquals(0, url.getParameters().size());

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880?version=1.0.0");

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals(null, url.getPath());

+        assertEquals(1, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(3, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+        assertEquals("noValue", url.getParameter("noValue"));

+    }

+

+    @Test

+    public void test_valueOf_Exception_noProtocol() throws Exception {

+        try {

+            URL.valueOf("://1.2.3.4:8080/path");

+            fail();

+        } catch (IllegalStateException expected) {

+            assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", expected.getMessage());

+        }

+    }

+

+    @Test

+    public void test_getAddress() throws Exception {

+        URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");

+        assertEquals("10.20.130.230:20880", url1.getAddress());

+    }

+    

+    @Test

+	public void test_getAbsolutePath() throws Exception {

+	    URL url = new URL("p1", "1.2.2.2",  33);

+	    assertEquals(null, url.getAbsolutePath());

+	    

+	    url = new URL("file", null, 90, "/home/user1/route.js");

+        assertEquals("/home/user1/route.js", url.getAbsolutePath());

+	}

+	

+	@Test

+    public void test_equals() throws Exception {

+        URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");

+        

+        Map<String, String> params = new HashMap<String, String>();

+        params.put("version", "1.0.0");

+        params.put("application", "morgan");

+        URL url2 = new URL("dubbo", "admin", "hello1234", "10.20.130.230", 20880, "context/path", params);

+        

+        assertEquals(url1, url2);

+    }

+	

+	@Test

+	public void test_toString() throws Exception {

+	    URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");

+	    assertThat(url1.toString(), anyOf(

+	            equalTo("dubbo://10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),

+	            equalTo("dubbo://10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))

+	            );

+	}

+	

+	@Test

+	public void test_toFullString() throws Exception {

+        URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");

+        assertThat(url1.toFullString(), anyOf(

+                equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),

+                equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))

+                );

+	}

+	

+	@Test

+    public void test_set_methods() throws Exception {

+	    URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");

+	    

+	    url = url.setHost("host");

+	    

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("host", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+        

+        url = url.setPort(1);

+        

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("host", url.getHost());

+        assertEquals(1, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+        

+        url = url.setPath("path");

+        

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("host", url.getHost());

+        assertEquals(1, url.getPort());

+        assertEquals("path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+	    

+        url = url.setProtocol("protocol");

+        

+        assertEquals("protocol", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("host", url.getHost());

+        assertEquals(1, url.getPort());

+        assertEquals("path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+        

+        url = url.setUsername("username");

+        

+        assertEquals("protocol", url.getProtocol());

+        assertEquals("username", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("host", url.getHost());

+        assertEquals(1, url.getPort());

+        assertEquals("path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));

+        

+        url = url.setPassword("password");

+        

+        assertEquals("protocol", url.getProtocol());

+        assertEquals("username", url.getUsername());

+        assertEquals("password", url.getPassword());

+        assertEquals("host", url.getHost());

+        assertEquals(1, url.getPort());

+        assertEquals("path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("1.0.0", url.getParameter("version"));

+        assertEquals("morgan", url.getParameter("application"));
+	}

+	

+	@Test

+    public void test_removeParameters() throws Exception {

+	    URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");

+	    

+	    url = url.removeParameter("version");

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(3, url.getParameters().size());

+        assertEquals("morgan", url.getParameter("application"));

+        assertEquals("v1", url.getParameter("k1"));

+        assertEquals("v2", url.getParameter("k2"));

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");

+        url = url.removeParameters("version", "application");

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("v1", url.getParameter("k1"));

+        assertEquals("v2", url.getParameter("k2"));

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");

+        url = url.removeParameters(Arrays.asList("version", "application"));

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("v1", url.getParameter("k1"));

+        assertEquals("v2", url.getParameter("k2"));

+	}

+	

+	@Test

+    public void test_addParameters() throws Exception {

+	    URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");

+        url = url.addParameters(CollectionUtils.toStringMap("k1", "v1", "k2", "v2"));

+        

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(3, url.getParameters().size());

+        assertEquals("morgan", url.getParameter("application"));

+        assertEquals("v1", url.getParameter("k1"));

+        assertEquals("v2", url.getParameter("k2"));

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");

+        url = url.addParameters("k1", "v1", "k2", "v2", "application", "xxx");

+        

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(3, url.getParameters().size());

+        assertEquals("xxx", url.getParameter("application"));

+        assertEquals("v1", url.getParameter("k1"));

+        assertEquals("v2", url.getParameter("k2"));

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");

+        url = url.addParametersIfAbsent(CollectionUtils.toStringMap("k1", "v1", "k2", "v2", "application", "xxx"));

+        

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(3, url.getParameters().size());

+        assertEquals("morgan", url.getParameter("application"));

+        assertEquals("v1", url.getParameter("k1"));

+        assertEquals("v2", url.getParameter("k2"));

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");

+        url = url.addParameter("k1", "v1");

+        

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(2, url.getParameters().size());

+        assertEquals("morgan", url.getParameter("application"));

+        assertEquals("v1", url.getParameter("k1"));

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");

+        url = url.addParameter("application", "xxx");

+        

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(1, url.getParameters().size());

+        assertEquals("xxx", url.getParameter("application"));

+        

+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");

+        url = url.addParameterIfAbsent("application", "xxx");

+        

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("admin", url.getUsername());

+        assertEquals("hello1234", url.getPassword());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("context/path", url.getPath());

+        assertEquals(1, url.getParameters().size());

+        assertEquals("morgan", url.getParameter("application"));

+	}

+	

+    @Test

+	public void test_windowAbsolutePathBeginWithSlashIsValid() throws Exception {

+	    final String osProperty = System.getProperties().getProperty("os.name");

+	    if(!osProperty.toLowerCase().contains("windows")) return;

+	    

+	    System.out.println("Test Windows valid path string.");

+	    

+	    File f0 = new File("C:/Windows");

+	    File f1 = new File("/C:/Windows");

+	    

+	    File f2 = new File("C:\\Windows");

+	    File f3 = new File("/C:\\Windows");

+	    File f4 = new File("\\C:\\Windows");

+	    

+	    assertEquals(f0, f1);

+	    assertEquals(f0, f2);

+	    assertEquals(f0, f3);

+	    assertEquals(f0, f4);

+	}

+	

+    @Test

+    public void test_javaNetUrl() throws Exception {

+        java.net.URL url = new java.net.URL("http://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan#anchor1");

+        

+        assertEquals("http", url.getProtocol());

+        assertEquals("admin:hello1234", url.getUserInfo());

+        assertEquals("10.20.130.230", url.getHost());

+        assertEquals(20880, url.getPort());

+        assertEquals("/context/path", url.getPath()); 

+        assertEquals("version=1.0.0&application=morgan", url.getQuery());

+        assertEquals("anchor1", url.getRef());

+        

+        assertEquals("admin:hello1234@10.20.130.230:20880", url.getAuthority());

+        assertEquals("/context/path?version=1.0.0&application=morgan", url.getFile());

+    }

+

+    @Test

+    public void test_Anyhost() throws Exception {

+        URL url = URL.valueOf("dubbo://0.0.0.0:20880");

+        assertEquals("true", url.getParameter("anyhost"));

+    }

+    

+    @Test

+    public void test_Localhost() throws Exception {

+        URL url = URL.valueOf("dubbo://127.0.0.1:20880");

+        assertEquals("true", url.getParameter("localhost"));

+        

+        url = URL.valueOf("dubbo://localhost:20880");

+        assertEquals("true", url.getParameter("localhost"));

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/ClassGeneratorTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/ClassGeneratorTest.java
new file mode 100644
index 0000000..420aa2c
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/ClassGeneratorTest.java
@@ -0,0 +1,79 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;
+
+import java.lang.reflect.Field;
+
+import junit.framework.TestCase;
+
+public class ClassGeneratorTest extends TestCase
+{
+	@SuppressWarnings("unchecked")
+	public void testMain() throws Exception
+	{
+		Bean b = new Bean();
+		Field fname = null, fs[] = Bean.class.getDeclaredFields();
+		for( Field f : fs )
+		{
+			f.setAccessible(true);
+			if( f.getName().equals("name") )
+				fname = f;
+		}
+
+		ClassGenerator cg = ClassGenerator.newInstance();
+		cg.setClassName(Bean.class.getName() + "$Builder");
+		cg.addInterface(Builder.class);
+
+		cg.addField("public static java.lang.reflect.Field FNAME;");
+
+		cg.addMethod("public Object getName("+Bean.class.getName()+" o){ boolean[][][] bs = new boolean[0][][]; return (String)FNAME.get($1); }");
+		cg.addMethod("public void setName("+Bean.class.getName()+" o, Object name){ FNAME.set($1, $2); }");
+
+		cg.addDefaultConstructor();
+		Class<?> cl = cg.toClass();
+		cl.getField("FNAME").set(null, fname);
+
+		System.out.println(cl.getName());
+		Builder<String> builder = (Builder<String>)cl.newInstance();
+		System.out.println(b.getName());
+		builder.setName(b, "ok");
+		System.out.println(b.getName());
+	}
+}
+
+class Bean
+{
+	int age = 30;
+
+	private String name = "qianlei";
+
+	public int getAge()
+	{
+		return age;
+	}
+
+	public String getName()
+	{
+		return name;
+	}
+}
+
+interface Builder<T>
+{
+	T getName(Bean bean);
+
+	void setName(Bean bean, T name);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/MixinTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/MixinTest.java
new file mode 100644
index 0000000..2f2e4df
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/MixinTest.java
@@ -0,0 +1,70 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;
+
+import com.alibaba.dubbo.common.bytecode.Mixin;
+
+import junit.framework.TestCase;
+
+public class MixinTest extends TestCase
+{
+	public void testMain() throws Exception
+	{
+		Mixin mixin = Mixin.mixin(new Class[]{ I1.class, I2.class, I3.class }, new Class[]{ C1.class, C2.class });
+		Object o = mixin.newInstance(new Object[]{ new C1(), new C2() });
+		assertEquals(o instanceof I1, true);
+		assertEquals(o instanceof I2, true);
+		assertEquals(o instanceof I3, true);
+		((I1)o).m1();
+		((I2)o).m2();
+		((I3)o).m3();
+	}
+
+	interface I1{ void m1(); }
+	interface I2{ void m2(); }
+	interface I3{ void m3(); }
+
+	class C1 implements Mixin.MixinAware
+	{
+		public void m1()
+		{
+			System.out.println("c1.m1();");
+		}
+
+		public void m2()
+		{
+			System.out.println("c1.m2();");
+		}
+
+		public void setMixinInstance(Object mi)
+		{
+			System.out.println("setMixinInstance:" + mi);
+		}
+	}
+
+	class C2 implements Mixin.MixinAware
+	{
+		public void m3()
+		{
+			System.out.println("c2.m3();");
+		}
+
+		public void setMixinInstance(Object mi)
+		{
+			System.out.println("setMixinInstance:" + mi);
+		}
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/ProxyTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/ProxyTest.java
new file mode 100644
index 0000000..eef0bce
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/ProxyTest.java
@@ -0,0 +1,57 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.bytecode.Proxy;
+
+import junit.framework.TestCase;
+
+public class ProxyTest extends TestCase
+{
+	public void testMain() throws Exception
+	{
+		Proxy proxy = Proxy.getProxy(ITest.class, ITest.class);
+		ITest instance = (ITest)proxy.newInstance(new InvocationHandler(){
+			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+			{
+				if( "getName".equals(method.getName()) )
+				{
+					assertEquals(args.length, 0);
+				}
+				else if( "setName".equals(method.getName()) )
+				{
+					assertEquals(args.length, 2);
+					assertEquals(args[0], "qianlei");
+					assertEquals(args[1], "hello");
+				}
+				return null;
+			}
+		});

+		

+		assertNull(instance.getName());
+		instance.setName("qianlei", "hello");
+	}
+
+	public static interface ITest
+	{
+		String getName();
+
+		void setName(String name, String name2);
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/WrapperTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/WrapperTest.java
new file mode 100644
index 0000000..a7b7503
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/bytecode/WrapperTest.java
@@ -0,0 +1,113 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.bytecode;
+
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+
+import junit.framework.TestCase;
+
+public class WrapperTest extends TestCase
+{
+	public void testMain() throws Exception
+	{
+		Wrapper w = Wrapper.getWrapper(I1.class);
+		String[] ns = w.getDeclaredMethodNames();
+		assertEquals(ns.length, 5);
+		ns = w.getMethodNames();
+		assertEquals(ns.length, 6);
+
+		Object obj = new Impl1();
+		assertEquals(w.getPropertyValue(obj, "name"), "you name");
+
+		w.setPropertyValue(obj, "name", "changed");
+		assertEquals(w.getPropertyValue(obj, "name"), "changed");
+

+		w.invokeMethod(obj, "hello", new Class<?>[] {String.class}, new Object[]{ "qianlei" });

+	}

+	

+	// bug: DUBBO-132

+	public void test_unwantedArgument() throws Exception {

+	    Wrapper w = Wrapper.getWrapper(I1.class);

+	    Object obj = new Impl1();

+        try {

+            w.invokeMethod(obj, "hello", new Class<?>[] { String.class, String.class },

+                    new Object[] { "qianlei", "badboy" });

+            fail();

+        } catch (NoSuchMethodException expected) {

+        }

+    }

+	
+
+	public static class Impl0
+	{
+		public float a,b,c;
+	}
+
+	public static interface I0
+	{
+		String getName();
+	}
+
+	public static interface I1 extends I0
+	{
+		void setName(String name);
+
+		void hello(String name);
+
+		int showInt(int v);
+
+		void setFloat(float f);
+
+		float getFloat();

+	}
+
+	public static class Impl1 implements I1
+	{
+		private String name = "you name";
+
+		private float fv = 0;
+
+		public String getName()
+		{
+			return name;
+		}
+
+		public void setName(String name)
+		{
+			this.name = name;
+		}
+
+		public void hello(String name)
+		{
+			System.out.println("hello " + name);
+		}
+
+		public int showInt(int v)
+		{
+			return v;
+		}
+
+		public float getFloat()
+		{
+			return fv;
+		}
+
+		public void setFloat(float f)
+		{
+			fv = f;
+		}
+	}

+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ExtensionLoaderTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ExtensionLoaderTest.java
new file mode 100644
index 0000000..6f843ab
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ExtensionLoaderTest.java
@@ -0,0 +1,498 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.common.extensionloader;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.ActivateExt1Impl1;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.GroupActivateExtImpl;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl1;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl2;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.ValueActivateExtImpl;
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+import com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl1;
+import com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl2;
+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;
+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+import com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper1;
+import com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper2;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl2;
+import com.alibaba.dubbo.common.extensionloader.ext7.Ext7;
+import com.alibaba.dubbo.common.utils.LogUtil;
+
+/**
+ * @author ding.lid
+ */
+public class ExtensionLoaderTest {
+    @Test
+    public void test_getDefault() throws Exception {
+        Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getDefaultExtension();
+        assertThat(ext, instanceOf(Ext1Impl1.class));
+        
+        String name = ExtensionLoader.getExtensionLoader(Ext1.class).getDefaultExtensionName();
+        assertEquals("impl1", name);
+    }
+    
+    @Test
+    public void test_getDefault_NULL() throws Exception {
+        Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getDefaultExtension();
+        assertNull(ext);
+        
+        String name = ExtensionLoader.getExtensionLoader(Ext2.class).getDefaultExtensionName();
+        assertNull(name);
+    }
+    
+    @Test
+    public void test_getExtension() throws Exception {
+        assertTrue(ExtensionLoader.getExtensionLoader(Ext1.class).getExtension("impl1") instanceof Ext1Impl1);
+        assertTrue(ExtensionLoader.getExtensionLoader(Ext1.class).getExtension("impl2") instanceof Ext1Impl2);
+    }
+    
+    @Test
+    public void test_getExtension_WithWrapper() throws Exception {
+        Ext5NoAdaptiveMethod impl1 = ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getExtension("impl1");
+        assertThat(impl1, anyOf(instanceOf(Ext5Wrapper1.class), instanceOf(Ext5Wrapper2.class)));
+        
+        Ext5NoAdaptiveMethod impl2 = ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getExtension("impl2") ;
+        assertThat(impl2, anyOf(instanceOf(Ext5Wrapper1.class), instanceOf(Ext5Wrapper2.class)));
+        
+        
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+        int echoCount1 = Ext5Wrapper1.echoCount.get();
+        int echoCount2 = Ext5Wrapper2.echoCount.get();
+        int yellCount1 = Ext5Wrapper1.yellCount.get();
+        int yellCount2 = Ext5Wrapper2.yellCount.get();
+        
+        assertEquals("Ext5Impl1-echo", impl1.echo(url, "ha"));
+        assertEquals(echoCount1 + 1, Ext5Wrapper1.echoCount.get());
+        assertEquals(echoCount2 + 1, Ext5Wrapper2.echoCount.get());
+        assertEquals(yellCount1, Ext5Wrapper1.yellCount.get());
+        assertEquals(yellCount2, Ext5Wrapper2.yellCount.get());
+        
+        assertEquals("Ext5Impl2-yell", impl2.yell(url, "ha"));
+        assertEquals(echoCount1 + 1, Ext5Wrapper1.echoCount.get());
+        assertEquals(echoCount2 + 1, Ext5Wrapper2.echoCount.get());
+        assertEquals(yellCount1 + 1, Ext5Wrapper1.yellCount.get());
+        assertEquals(yellCount2 + 1, Ext5Wrapper2.yellCount.get());
+    }
+    
+    @Test
+    public void test_getExtension_ExceptionNoExtension() throws Exception {
+        try {
+            ExtensionLoader.getExtensionLoader(Ext1.class).getExtension("XXX");
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("No such extension com.alibaba.dubbo.common.extensionloader.ext1.Ext1 by name XXX"));
+        }
+    }
+    
+    @Test
+    public void test_getExtension_ExceptionNoExtension_NameOnWrapperNoAffact() throws Exception {
+        try {
+            ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getExtension("XXX");
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("No such extension com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod by name XXX"));
+        }
+    }
+    
+    @Test
+    public void test_getExtension_ExceptionNullArg() throws Exception {
+        try {
+            ExtensionLoader.getExtensionLoader(Ext1.class).getExtension(null);
+        } catch (IllegalArgumentException expected) {
+            assertThat(expected.getMessage(), containsString("Extension name == null"));
+        }
+    }
+    
+    @Test
+    public void test_hasExtension() throws Exception {
+        assertTrue(ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension("impl1"));
+        assertFalse(ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension("impl1,impl2"));
+        assertFalse(ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension("xxx"));
+        
+        try {
+            ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension(null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            assertThat(expected.getMessage(), containsString("Extension name == null"));
+        }
+    }
+    
+    @Test
+    public void test_getSupportedExtensions() throws Exception {
+        Set<String> exts = ExtensionLoader.getExtensionLoader(Ext1.class).getSupportedExtensions();
+        
+        Set<String> expected = new HashSet<String>();
+        expected.add("impl1");
+        expected.add("impl2");
+        expected.add("impl3");
+        
+        assertEquals(expected, exts);
+    }
+    
+    @Test
+    public void test_getSupportedExtensions_NoExtension() throws Exception {
+        try {
+            ExtensionLoader.getExtensionLoader(ExtensionLoaderTest.class).getSupportedExtensions();
+            fail();
+        } catch (IllegalArgumentException expected) {
+            assertThat(expected.getMessage(), 
+                    allOf(containsString("com.alibaba.dubbo.common.extensionloader.ExtensionLoaderTest"),
+                            containsString("is not extension"),
+                            containsString("WITHOUT @SPI Annotation")));
+       
+        }
+    }
+    
+    @Test
+    public void test_getAdaptiveExtension_defaultExtension() throws Exception {
+        Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+        Map<String, String> map = new HashMap<String, String>();
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+        String echo = ext.echo(url, "haha");
+        assertEquals("Ext1Impl1-echo", echo);
+    }
+
+    @Test
+    public void test_getAdaptiveExtension() throws Exception {
+        Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("ext1", "impl2");
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+        String echo = ext.echo(url, "haha");
+        assertEquals("Ext1Impl2-echo", echo);
+    }
+
+    @Test
+    public void test_getAdaptiveExtension_customizeKey() throws Exception {
+        Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("key2", "impl2");
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+        String echo = ext.yell(url, "haha");
+        assertEquals("Ext1Impl2-yell", echo);
+
+        url = url.addParameter("key1", "impl3"); // 注意: URL是值类型
+        echo = ext.yell(url, "haha");
+        assertEquals("Ext1Impl3-yell", echo);
+    }
+
+    @Test
+    public void test_getAdaptiveExtension_UrlNpe() throws Exception {
+        Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+        try {
+            ext.echo(null, "haha");
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertEquals("url == null", e.getMessage());
+        }
+    }
+    
+    @Test
+    public void test_getAdaptiveExtension_ExceptionWhenNoAdativeMethodOnInterface() throws Exception {
+        try {
+            ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getAdaptiveExtension();
+            fail();
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), 
+                    allOf(containsString("Can not create adaptive extenstion interface com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod"),
+                            containsString("No adaptive method on extension com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod, refuse to create the adaptive class")));
+        }
+        // 多次get,都会报错且相同
+        try {
+            ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getAdaptiveExtension();
+            fail();
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), 
+                    allOf(containsString("Can not create adaptive extenstion interface com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod"),
+                            containsString("No adaptive method on extension com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod, refuse to create the adaptive class")));
+        }
+    }
+
+    @Test
+    public void test_getAdaptiveExtension_ExceptionWhenNotAdativeMethod() throws Exception {
+        Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+        Map<String, String> map = new HashMap<String, String>();
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+        try {
+            ext.bang(url, 33);
+            fail();
+        } catch (UnsupportedOperationException expected) {
+            assertThat(expected.getMessage(), containsString("method "));
+            assertThat(
+                    expected.getMessage(),
+                    containsString("of interface com.alibaba.dubbo.common.extensionloader.ext1.Ext1 is not adaptive method!"));
+        }
+    }
+    
+    @Test
+    public void test_getAdaptiveExtension_ExceptionWhenNoUrlAttrib() throws Exception {
+        try {
+            ExtensionLoader.getExtensionLoader(Ext4.class).getAdaptiveExtension();
+            fail();
+        } catch (Exception expected) {
+            assertThat(expected.getMessage(), containsString("fail to create adative class for interface "));
+            assertThat(expected.getMessage(), containsString(": not found url parameter or url attribute in parameters of method "));
+        }
+    }
+    
+    @Test
+    public void test_getAdaptiveExtension_protocolKey() throws Exception {
+        Ext3 ext = ExtensionLoader.getExtensionLoader(Ext3.class).getAdaptiveExtension();
+    
+        Map<String, String> map = new HashMap<String, String>();
+        URL url = new URL("impl3", "1.2.3.4", 1010, "path1", map);
+        
+        String echo = ext.echo(url, "s");
+        assertEquals("Ext3Impl3-echo", echo);
+    
+        url = url.addParameter("key1", "impl2");
+        echo = ext.echo(url, "s");
+        assertEquals("Ext3Impl2-echo", echo);
+        
+        String yell = ext.yell(url, "d");
+        assertEquals("Ext3Impl3-yell", yell);
+    }
+    
+    
+    @Test
+    public void test_getAdaptiveExtension_lastProtocolKey() throws Exception {
+        Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+        
+        Map<String, String> map = new HashMap<String, String>();
+        URL url = new URL("impl1", "1.2.3.4", 1010, "path1", map);
+        String yell = ext.yell(url, "s");
+        assertEquals("Ext2Impl1-yell", yell);
+        
+        url = url.addParameter("key1", "impl2");
+        yell = ext.yell(url, "s");
+        assertEquals("Ext2Impl2-yell", yell);
+    }
+
+    @Test
+    public void test_urlHolder_getAdaptiveExtension() throws Exception {
+        Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+        
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("ext2", "impl1");
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+        
+        UrlHolder holder = new UrlHolder();
+        holder.setUrl(url);
+    
+        String echo = ext.echo(holder, "haha");
+        assertEquals("Ext2Impl1-echo", echo);
+    }
+
+    @Test
+    public void test_urlHolder_getAdaptiveExtension_noExtension() throws Exception {
+        Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+
+        UrlHolder holder = new UrlHolder();
+        holder.setUrl(url);
+        
+        try {
+            ext.echo(holder, "haha");
+            fail();
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Fail to get extension("));
+        }
+        
+        url = url.addParameter("ext2", "XXX");
+        holder.setUrl(url);
+        try {
+            ext.echo(holder, "haha");
+            fail();
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("No such extension"));
+        }
+    }
+
+    @Test
+    public void test_urlHolder_getAdaptiveExtension_UrlNpe() throws Exception {
+        Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+        try {
+            ext.echo(null, "haha");
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertEquals("com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder argument == null", e.getMessage());
+        }
+        
+        try {
+            ext.echo(new UrlHolder(), "haha");
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertEquals("com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder argument getUrl() == null", e.getMessage());
+        }
+    }
+
+    @Test
+    public void test_urlHolder_getAdaptiveExtension_ExceptionWhenNotAdativeMethod() throws Exception {
+        Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+        Map<String, String> map = new HashMap<String, String>();
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+        try {
+            ext.bang(url, 33);
+            fail();
+        } catch (UnsupportedOperationException expected) {
+            assertThat(expected.getMessage(), containsString("method "));
+            assertThat(
+                    expected.getMessage(),
+                    containsString("of interface com.alibaba.dubbo.common.extensionloader.ext2.Ext2 is not adaptive method!"));
+        }
+    }
+
+    @Test
+    public void test_urlHolder_getAdaptiveExtension_ExceptionWhenNameNotProvided() throws Exception {
+        Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+
+        UrlHolder holder = new UrlHolder();
+        holder.setUrl(url);
+        
+        try {
+            ext.echo(holder, "impl1");
+            fail();
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Fail to get extension("));
+        }
+        
+        url = url.addParameter("key1", "impl1");
+        holder.setUrl(url);
+        try {
+            ext.echo(holder, "haha");
+            fail();
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext2.Ext2) name from url"));
+        }
+    }
+    
+    @Test
+    public void test_getAdaptiveExtension_inject() throws Exception {
+        LogUtil.start();
+        Ext6 ext = ExtensionLoader.getExtensionLoader(Ext6.class).getAdaptiveExtension();
+
+        URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+        url = url.addParameters("ext6", "impl1");
+        
+        assertEquals("Ext6Impl1-echo-Ext1Impl1-echo", ext.echo(url, "ha"));
+        
+        Assert.assertTrue("can not find error.", LogUtil.checkNoError());
+        LogUtil.stop();
+        
+        url = url.addParameters("ext1", "impl2");
+        assertEquals("Ext6Impl1-echo-Ext1Impl2-echo", ext.echo(url, "ha"));
+        
+    }
+    
+    @Test
+    public void test_getAdaptiveExtension_InjectNotExtFail() throws Exception {
+        Ext6 ext = ExtensionLoader.getExtensionLoader(Ext6.class).getExtension("impl2");
+        
+        Ext6Impl2 impl = (Ext6Impl2) ext;
+        assertNull(impl.getList());
+    }
+    
+    @Test
+    public void test_InitError() throws Exception {
+        ExtensionLoader<Ext7> loader = ExtensionLoader.getExtensionLoader(Ext7.class);
+        
+        loader.getExtension("ok");
+        
+        try {
+            loader.getExtension("error");
+            fail();
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Failed to load extension class(interface: interface com.alibaba.dubbo.common.extensionloader.ext7.Ext7"));
+            assertThat(expected.getCause(), instanceOf(ExceptionInInitializerError.class));
+        }
+    }
+
+    @Test
+    public void testLoadActivateExtension() throws Exception {
+        // test default
+        URL url = URL.valueOf("test://localhost/test");
+        List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+                .getActivateExtension(url, new String[]{}, "default_group");
+        Assert.assertEquals(1, list.size());
+        Assert.assertTrue(list.get(0).getClass() == ActivateExt1Impl1.class);
+
+        // test group
+        url = url.addParameter(Constants.GROUP_KEY, "group1");
+        list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+                .getActivateExtension(url, new String[]{}, "group1");
+        Assert.assertEquals(1, list.size());
+        Assert.assertTrue(list.get(0).getClass() == GroupActivateExtImpl.class);
+
+        // test value
+        url = url.removeParameter(Constants.GROUP_KEY);
+        url = url.addParameter(Constants.GROUP_KEY, "value");
+        url = url.addParameter("value", "value");
+        list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+                .getActivateExtension(url, new String[]{}, "value");
+        Assert.assertEquals(1, list.size());
+        Assert.assertTrue(list.get(0).getClass() == ValueActivateExtImpl.class);
+
+        // test order
+        url = URL.valueOf("test://localhost/test");
+        url = url.addParameter(Constants.GROUP_KEY, "order");
+        list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+                .getActivateExtension(url, new String[]{}, "order");
+        Assert.assertEquals(2, list.size());
+        Assert.assertTrue(list.get(0).getClass() == OrderActivateExtImpl1.class);
+        Assert.assertTrue(list.get(1).getClass() == OrderActivateExtImpl2.class);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/ActivateExt1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/ActivateExt1.java
new file mode 100644
index 0000000..d8b78f3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/ActivateExt1.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.common.extensionloader.activate;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@SPI("impl1")
+public interface ActivateExt1 {
+    String echo(String msg);
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ActivateExt1Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ActivateExt1Impl1.java
new file mode 100644
index 0000000..577d6c7
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ActivateExt1Impl1.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(group = {"default_group"})
+public class ActivateExt1Impl1 implements ActivateExt1 {
+    public String echo(String msg) {
+        return msg;
+    }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/GroupActivateExtImpl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/GroupActivateExtImpl.java
new file mode 100644
index 0000000..d46ebf0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/GroupActivateExtImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(group = {"group1", "group2"})
+public class GroupActivateExtImpl implements ActivateExt1 {
+
+    public String echo(String msg) {
+        return msg;
+    }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl1.java
new file mode 100644
index 0000000..544041e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl1.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(order = 1, group = {"order"})
+public class OrderActivateExtImpl1 implements ActivateExt1 {
+
+    public String echo(String msg) {
+        return msg;
+    }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl2.java
new file mode 100644
index 0000000..3d9dd94
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl2.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(order = 2, group = {"order"})
+public class OrderActivateExtImpl2 implements ActivateExt1 {
+
+    public String echo(String msg) {
+        return msg;
+    }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ValueActivateExtImpl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ValueActivateExtImpl.java
new file mode 100644
index 0000000..14328e6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ValueActivateExtImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(value = {"value"}, group = {"value"})
+public class ValueActivateExtImpl implements ActivateExt1 {
+
+    public String echo(String msg) {
+        return msg;
+    }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/Ext1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/Ext1.java
new file mode 100644
index 0000000..fcc9ba3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/Ext1.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext1;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * @author ding.lid
+ */
+@SPI("impl1")
+public interface Ext1 {
+    @Adaptive
+    String echo(URL url, String s);
+    
+    @Adaptive({"key1", "key2"})
+    String yell(URL url, String s);
+    
+    String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl1.java
new file mode 100644
index 0000000..3524440
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl1.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext1.impl;
+
+import com.alibaba.dubbo.common.Extension;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;

+
+/**
+ * @author ding.lid
+ */

+@Extension("impl1")
+public class Ext1Impl1 implements Ext1 {
+    public String echo(URL url, String s) {
+        return "Ext1Impl1-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext1Impl1-yell";
+    }
+    
+    public String bang(URL url, int i) {
+        return "bang1";
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.java
new file mode 100644
index 0000000..12e9265
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext1.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;

+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext1Impl2 implements Ext1 {
+    public String echo(URL url, String s) {
+        return "Ext1Impl2-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext1Impl2-yell";
+    }
+
+    public String bang(URL url, int i) {
+        return "bang2";
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.java
new file mode 100644
index 0000000..86e18de
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext1.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;

+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext1Impl3 implements Ext1 {
+    public String echo(URL url, String s) {
+        return "Ext1Impl3-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext1Impl3-yell";
+    }
+
+    public String bang(URL url, int i) {
+        return "bang3";
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.java
new file mode 100644
index 0000000..7978dcd
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext2;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * 无Default
+ * 
+ * @author ding.lid
+ */

+@SPI
+public interface Ext2 {
+    @Adaptive
+    String echo(UrlHolder holder, String s);
+    
+    @Adaptive({"key1", "protocol"})
+    String yell(URL url, String s);
+    
+    String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/UrlHolder.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/UrlHolder.java
new file mode 100644
index 0000000..4c8aa82
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/UrlHolder.java
@@ -0,0 +1,64 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext2;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class UrlHolder {
+    private Double Num;
+    
+    private URL url;
+    
+    private String name;
+    
+    private int age;
+    
+    public Double getNum() {
+        return Num;
+    }
+
+    public void setNum(Double num) {
+        Num = num;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    public void setUrl(URL url) {
+        this.url = url;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.java
new file mode 100644
index 0000000..5eb0b8c
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext2.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;

+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;

+
+/**
+ * @author ding.lid
+ */
+public class Ext2Impl1 implements Ext2 {
+    public String echo(UrlHolder holder, String s) {
+        return "Ext2Impl1-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext2Impl1-yell";
+    }
+    
+    public String bang(URL url, int i) {
+        return "bang1";
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.java
new file mode 100644
index 0000000..3597345
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.java
@@ -0,0 +1,39 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext2.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;

+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;

+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext2Impl2 implements Ext2 {
+    public String echo(UrlHolder holder, String s) {
+        return "Ext2Impl2-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext2Impl2-yell";
+    }
+
+    public String bang(URL url, int i) {
+        return "bang2";
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.java
new file mode 100644
index 0000000..8f1805e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.java
@@ -0,0 +1,39 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext2.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;

+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;

+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext2Impl3 implements Ext2 {
+    public String echo(UrlHolder holder, String s) {
+        return "Ext2Impl3-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext2Impl3-yell";
+    }
+
+    public String bang(URL url, int i) {
+        return "bang3";
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/Ext3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/Ext3.java
new file mode 100644
index 0000000..38bec13
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/Ext3.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext3;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * @author ding.lid
+ */
+@SPI("impl3")
+public interface Ext3 {
+    @Adaptive({"key1", "protocol"})
+    String echo(URL url, String s);
+    
+    @Adaptive({"protocol", "key2"})
+    String yell(URL url, String s);
+    
+    String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.java
new file mode 100644
index 0000000..9d8e29b
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;

+
+/**
+ * @author ding.lid
+ */
+public class Ext3Impl1 implements Ext3 {
+    public String echo(URL url, String s) {
+        return "Ext3Impl3-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext3Impl3-yell";
+    }
+    
+    public String bang(URL url, int i) {
+        return "bang1";
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.java
new file mode 100644
index 0000000..5953c93
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;

+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext3Impl2 implements Ext3 {
+    public String echo(URL url, String s) {
+        return "Ext3Impl2-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext3Impl2-yell";
+    }
+
+    public String bang(URL url, int i) {
+        return "bang2";
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.java
new file mode 100644
index 0000000..ae5b6d1
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;

+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext3Impl3 implements Ext3 {
+    public String echo(URL url, String s) {
+        return "Ext3Impl3-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext3Impl3-yell";
+    }
+
+    public String bang(URL url, int i) {
+        return "bang3";
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/Ext4.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/Ext4.java
new file mode 100644
index 0000000..b255b9b
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/Ext4.java
@@ -0,0 +1,30 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext4;
+
+import java.util.List;

+

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * @author ding.lid
+ */
+@SPI("impl1")
+public interface Ext4 {
+    @Adaptive
+    String bark(String name, List<Object> list); // 没有URL参数的方法
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.java
new file mode 100644
index 0000000..6e72d8d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.java
@@ -0,0 +1,29 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext4.impl;
+
+import java.util.List;

+

+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;

+
+/**
+ * @author ding.lid
+ */
+public class Ext4Impl1 implements Ext4 {
+    public String bark(String name, List<Object> list) {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.java
new file mode 100644
index 0000000..bf98111
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.java
@@ -0,0 +1,44 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext4.impl;
+
+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;

+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext4Impl2 implements Ext4 {
+    public String echo(URL url, String s) {
+        return "Ext3Impl2-echo";
+    }
+    
+    public String yell(URL url, String s) {
+        return "Ext3Impl2-yell";
+    }
+
+    public String bang(URL url, int i) {
+        return "bang2";
+    }
+    
+    public String bark(String name, List<Object> list) {
+        return null;
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/Ext5NoAdaptiveMethod.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/Ext5NoAdaptiveMethod.java
new file mode 100644
index 0000000..955a4b1
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/Ext5NoAdaptiveMethod.java
@@ -0,0 +1,31 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext5;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * @author ding.lid
+ */
+@SPI("impl1")
+public interface Ext5NoAdaptiveMethod {
+    String echo(URL url, String s);
+    
+    String yell(URL url, String s);
+    
+    String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.java
new file mode 100644
index 0000000..f674af0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext5.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;

+
+/**
+ * @author ding.lid
+ */
+public class Ext5Impl1 implements Ext5NoAdaptiveMethod {
+    public String echo(URL url, String s) {
+        return "Ext5Impl1-echo";
+    }
+
+    public String yell(URL url, String s) {
+        return "Ext5Impl1-yell";
+    }
+
+    public String bang(URL url, int i) {
+        return "impl1";
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.java
new file mode 100644
index 0000000..a6c6cda
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext5.impl;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;

+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext5Impl2 implements Ext5NoAdaptiveMethod {
+    public String echo(URL url, String s) {
+        return "Ext5Impl2-echo";
+    }
+
+    public String yell(URL url, String s) {
+        return "Ext5Impl2-yell";
+    }
+
+    public String bang(URL url, int i) {
+        return "impl2";
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java
new file mode 100644
index 0000000..a151777
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext5.impl;
+
+import java.util.concurrent.atomic.AtomicInteger;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;

+
+/**
+ * @author ding.lid
+ */
+public class Ext5Wrapper1 implements Ext5NoAdaptiveMethod {
+    Ext5NoAdaptiveMethod instance;
+    
+    public static AtomicInteger echoCount = new AtomicInteger();
+    public static AtomicInteger yellCount = new AtomicInteger();
+    public static AtomicInteger bangCount = new AtomicInteger();
+    
+    public Ext5Wrapper1(Ext5NoAdaptiveMethod instance) {
+        this.instance = instance;
+    }
+    
+    public String echo(URL url, String s) {
+        echoCount.incrementAndGet();
+        return instance.echo(url, s);
+    }
+
+    public String yell(URL url, String s) {
+        yellCount.incrementAndGet();
+        return instance.yell(url, s);
+    }
+
+    public String bang(URL url, int i) {
+        bangCount.incrementAndGet();
+        return instance.bang(url, i);
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper2.java
new file mode 100644
index 0000000..ffb1dcb
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper2.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext5.impl;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ */
+public class Ext5Wrapper2 implements Ext5NoAdaptiveMethod {
+    Ext5NoAdaptiveMethod instance;
+    
+    public static AtomicInteger echoCount = new AtomicInteger();
+    public static AtomicInteger yellCount = new AtomicInteger();
+    public static AtomicInteger bangCount = new AtomicInteger();
+    
+    public Ext5Wrapper2(Ext5NoAdaptiveMethod instance) {
+        this.instance = instance;
+    }
+    
+    public String echo(URL url, String s) {
+        echoCount.incrementAndGet();
+        return instance.echo(url, s);
+    }
+
+    public String yell(URL url, String s) {
+        yellCount.incrementAndGet();
+        return instance.yell(url, s);
+    }
+
+    public String bang(URL url, int i) {
+        bangCount.incrementAndGet();
+        return instance.bang(url, i);
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Dao.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Dao.java
new file mode 100644
index 0000000..9884b6b
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Dao.java
@@ -0,0 +1,6 @@
+package com.alibaba.dubbo.common.extensionloader.ext6_inject;
+
+
+public interface Dao {
+    public void update();
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Ext6.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Ext6.java
new file mode 100644
index 0000000..2562e57
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Ext6.java
@@ -0,0 +1,31 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext6_inject;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * 无Default
+ * 
+ * @author ding.lid
+ */

+@SPI
+public interface Ext6 {
+    @Adaptive
+    String echo(URL url, String s);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/DaoImpl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/DaoImpl.java
new file mode 100644
index 0000000..89a7cde
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/DaoImpl.java
@@ -0,0 +1,9 @@
+package com.alibaba.dubbo.common.extensionloader.ext6_inject.impl;
+
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Dao;
+
+public class DaoImpl implements Dao {
+    public void update(){
+        
+    }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.java
new file mode 100644
index 0000000..d3a64f1
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext6_inject.impl;
+
+import junit.framework.Assert;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;

+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Dao;

+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;

+
+/**
+ * @author ding.lid
+ */
+public class Ext6Impl1 implements Ext6 {
+    Ext1 ext1;

+    public Dao obj;

+    

+    public void setDao(Dao obj){

+        Assert.assertNotNull("inject extension instance can not be null", obj);

+        Assert.fail();

+    }
+    
+    public void setExt1(Ext1 ext1) {
+        this.ext1 = ext1;
+    }
+
+    public String echo(URL url, String s) {
+        return "Ext6Impl1-echo-" + ext1.echo(url, s);
+    }

+    
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.java
new file mode 100644
index 0000000..4b93767
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.java
@@ -0,0 +1,41 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.extensionloader.ext6_inject.impl;
+
+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;

+
+/**
+ * @author ding.lid
+ */
+public class Ext6Impl2 implements Ext6 {
+    List<String> list;
+
+    public List<String> getList() {
+        return list;
+    }
+
+    public void setList(List<String> list) {
+        this.list = list;
+    }
+
+    public String echo(URL url, String s) {
+        throw new UnsupportedOperationException();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/Ext7.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/Ext7.java
new file mode 100644
index 0000000..5766719
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/Ext7.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.common.extensionloader.ext7;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * 用于测试:
+ * DUBBO-144 扩展点加载失败(如依赖的三方库运行时没有),如扩展点没有用到,则加载不要报错(在使用到时报错)
+ * 
+ * @author ding.lid
+ */
+@SPI
+public interface Ext7 {
+    @Adaptive
+    String echo(URL url, String s);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7Impl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7Impl.java
new file mode 100644
index 0000000..3eedd08
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7Impl.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.common.extensionloader.ext7.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext7.Ext7;
+
+/**
+ * @author ding.lid
+ */
+public class Ext7Impl implements Ext7 {
+    public String echo(URL url, String s) {
+        return "";
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7InitErrorImpl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7InitErrorImpl.java
new file mode 100644
index 0000000..0edba0e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7InitErrorImpl.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.common.extensionloader.ext7.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext7.Ext7;
+
+/**
+ * @author ding.lid
+ */
+public class Ext7InitErrorImpl implements Ext7 {
+    
+    static {
+        if(true) {
+            throw new RuntimeException("intended!");
+        }
+    }
+
+    public String echo(URL url, String s) {
+        return "";
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/BytesTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/BytesTest.java
new file mode 100644
index 0000000..8e6c97d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/BytesTest.java
@@ -0,0 +1,122 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.io;
+
+import org.junit.Assert;
+
+import junit.framework.TestCase;
+
+public class BytesTest extends TestCase
+{
+	private static final byte[] b1 = "adpfioha;eoh;aldfadl;kfadslkfdajfio123431241235123davas;odvwe;lmzcoqpwoewqogineopwqihwqetup\n\tejqf;lajsfd中文字符0da0gsaofdsf==adfasdfs".getBytes();
+
+	public void testMain() throws Exception
+	{
+		short s = (short)0xabcd;
+		assertEquals(s, Bytes.bytes2short(Bytes.short2bytes(s)) );
+
+		int i = 198284;
+		assertEquals(i, Bytes.bytes2int(Bytes.int2bytes(i)) );
+
+		long l = 13841747174l;
+		assertEquals(l, Bytes.bytes2long(Bytes.long2bytes(l)) );
+
+		float f = 1.3f;
+		assertEquals(f, Bytes.bytes2float(Bytes.float2bytes(f)) );
+
+		double d = 11213.3;
+		assertEquals(d, Bytes.bytes2double(Bytes.double2bytes(d)) );
+
+		assertSame(Bytes.int2bytes(i), int2bytes(i));
+		assertSame(Bytes.long2bytes(l), long2bytes(l));
+	}
+
+	public void testBase64() throws Exception
+	{
+		String str = Bytes.bytes2base64(b1);
+		byte[] b2 = Bytes.base642bytes(str);
+		assertSame(b1, b2);
+	}
+    
+	static byte[] bytes1 = {3,12,14,41,12,2,3,12,4,67,23};
+	static byte[] bytes2 = {3,12,14,41,12,2,3,12,4,67};
+	// 把失败的情况,失败Case加的防护网:
+	// 当有填充字符时,会失败!
+    public void testBase64_s2b2s_FailCaseLog() throws Exception {
+        String s1 = Bytes.bytes2base64(bytes1);
+        byte[] out1 = Bytes.base642bytes(s1);
+        Assert.assertArrayEquals(bytes1, out1);
+        
+
+        String s2 = Bytes.bytes2base64(bytes2);
+        byte[] out2 = Bytes.base642bytes(s2);
+        Assert.assertArrayEquals(bytes2, out2);
+    }
+
+	public void testHex() throws Exception
+	{
+		String str = Bytes.bytes2hex(b1);
+		assertSame(b1, Bytes.hex2bytes(str));
+	}
+
+	private static void assertSame(byte[] b1, byte[] b2)
+	{
+		assertEquals(b1.length, b2.length);
+		for(int i=0;i<b1.length;i++)
+			assertEquals(b1[i], b2[i]);
+	}
+
+	// tb-remoting codec method.
+	static public byte[] int2bytes(int x) {
+        byte[] bb = new byte[4];
+        bb[0] = (byte) (x >> 24);
+        bb[1] = (byte) (x >> 16);
+        bb[2] = (byte) (x >> 8);
+        bb[3] = (byte) (x >> 0);
+        return bb;
+    }
+
+    static public int bytes2int(byte[] bb, int idx) {
+        return ((bb[idx + 0] & 0xFF) << 24)
+             | ((bb[idx + 1] & 0xFF) << 16)
+             | ((bb[idx + 2] & 0xFF) << 8)
+             | ((bb[idx + 3] & 0xFF) << 0);
+    }
+
+    static public byte[] long2bytes(long x) {
+        byte[] bb = new byte[8];
+        bb[0] = (byte) (x >> 56);
+        bb[1] = (byte) (x >> 48);
+        bb[2] = (byte) (x >> 40);
+        bb[3] = (byte) (x >> 32);
+        bb[4] = (byte) (x >> 24);
+        bb[5] = (byte) (x >> 16);
+        bb[6] = (byte) (x >> 8);
+        bb[7] = (byte) (x >> 0);
+        return bb;
+    }
+
+    static public long bytes2long(byte[] bb, int idx) {
+        return (((long) bb[idx + 0] & 0xFF) << 56)
+             | (((long) bb[idx + 1] & 0xFF) << 48)
+             | (((long) bb[idx + 2] & 0xFF) << 40)
+             | (((long) bb[idx + 3] & 0xFF) << 32)
+             | (((long) bb[idx + 4] & 0xFF) << 24)
+             | (((long) bb[idx + 5] & 0xFF) << 16)
+             | (((long) bb[idx + 6] & 0xFF) << 8)
+             | (((long) bb[idx + 7] & 0xFF) << 0);
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/StreamUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/StreamUtilsTest.java
new file mode 100644
index 0000000..71233e0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/StreamUtilsTest.java
@@ -0,0 +1,81 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.io;
+
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author ding.lid
+ */
+public class StreamUtilsTest {
+
+    @Test
+    public void testMarkSupportedInputStream() throws Exception {
+        InputStream is = StreamUtilsTest.class.getResourceAsStream("/StreamUtilsTest.txt");
+        assertEquals(10, is.available());
+        
+        is = new PushbackInputStream(is);
+        assertEquals(10, is.available());
+        assertFalse(is.markSupported());
+
+        is = StreamUtils.markSupportedInputStream(is);
+        assertEquals(10, is.available());
+        
+        is.mark(0);
+        assertEquals((int) '0', is.read());
+        assertEquals((int) '1', is.read());
+
+        is.reset();
+        assertEquals((int) '0', is.read());
+        assertEquals((int) '1', is.read());
+        assertEquals((int) '2', is.read());
+        
+        is.mark(0);
+        assertEquals((int) '3', is.read());
+        assertEquals((int) '4', is.read());
+        assertEquals((int) '5', is.read());
+        
+        is.reset();
+        assertEquals((int) '3', is.read());
+        assertEquals((int) '4', is.read());
+        
+        is.mark(0);
+        assertEquals((int) '5', is.read());
+        assertEquals((int) '6', is.read());
+        
+        is.reset();
+        assertEquals((int) '5', is.read());
+        assertEquals((int) '6', is.read());
+        assertEquals((int) '7', is.read());
+        assertEquals((int) '8', is.read());
+        assertEquals((int) '9', is.read());
+        assertEquals(-1, is.read());
+        assertEquals(-1, is.read());
+        
+        is.mark(0);
+        assertEquals(-1, is.read());
+        assertEquals(-1, is.read());
+        
+        is.reset();
+        assertEquals(-1, is.read());
+        assertEquals(-1, is.read());
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONReaderTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONReaderTest.java
new file mode 100644
index 0000000..b3330d6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONReaderTest.java
@@ -0,0 +1,45 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;
+
+import com.alibaba.dubbo.common.io.UnsafeStringReader;
+
+import junit.framework.TestCase;
+
+public class JSONReaderTest extends TestCase
+{
+	public void testMain() throws Exception
+	{
+		String json = "{ name: 'name', friends: [ 1, null, 3.2, ] }";
+		JSONReader reader = new JSONReader(new UnsafeStringReader(json));
+		assertEquals(reader.nextToken().type, JSONToken.LBRACE);
+		assertEquals(reader.nextToken().type, JSONToken.IDENT);
+		assertEquals(reader.nextToken().type, JSONToken.COLON);
+		assertEquals(reader.nextToken().type, JSONToken.STRING);
+		assertEquals(reader.nextToken().type, JSONToken.COMMA);
+		assertEquals(reader.nextToken().type, JSONToken.IDENT);
+		assertEquals(reader.nextToken().type, JSONToken.COLON);
+		assertEquals(reader.nextToken().type, JSONToken.LSQUARE);
+		assertEquals(reader.nextToken().type, JSONToken.INT);
+		assertEquals(reader.nextToken().type, JSONToken.COMMA);
+		assertEquals(reader.nextToken().type, JSONToken.NULL);
+		assertEquals(reader.nextToken().type, JSONToken.COMMA);
+		assertEquals(reader.nextToken().type, JSONToken.FLOAT);
+		assertEquals(reader.nextToken().type, JSONToken.COMMA);
+		assertEquals(reader.nextToken().type, JSONToken.RSQUARE);
+		assertEquals(reader.nextToken().type, JSONToken.RBRACE);
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONTest.java
new file mode 100644
index 0000000..337dba7
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONTest.java
@@ -0,0 +1,217 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+public class JSONTest extends TestCase
+{
+	public void testException() throws Exception {
+		MyException e = new MyException("001", "AAAAAAAA");
+		
+		StringWriter writer = new StringWriter();
+		JSON.json(e, writer);
+		String json = writer.getBuffer().toString();
+		System.out.println(json);
+		// Assert.assertEquals("{\"code\":\"001\",\"message\":\"AAAAAAAA\"}", json);
+		
+		StringReader reader = new StringReader(json);
+		MyException result = JSON.parse(reader, MyException.class);
+		Assert.assertEquals("001", result.getCode());
+		Assert.assertEquals("AAAAAAAA", result.getMessage());
+	}
+
+	@SuppressWarnings("unchecked")
+	public void testMap() throws Exception {
+		Map<String, String> map = new HashMap<String, String>();
+		map.put("aaa", "bbb");
+		
+		StringWriter writer = new StringWriter();
+		JSON.json(map, writer);
+		String json = writer.getBuffer().toString();
+		Assert.assertEquals("{\"aaa\":\"bbb\"}", json);
+		
+		StringReader reader = new StringReader(json);
+		Map<String, String> result = JSON.parse(reader, Map.class);
+		Assert.assertEquals("bbb", result.get("aaa"));
+	}
+
+	@SuppressWarnings("unchecked")
+	public void testMapArray() throws Exception {
+		Map<String, String> map = new HashMap<String, String>();
+		map.put("aaa", "bbb");
+
+		StringWriter writer = new StringWriter();
+		JSON.json(new Object[] {map}, writer); // args
+		String json = writer.getBuffer().toString();
+		Assert.assertEquals("[{\"aaa\":\"bbb\"}]", json);
+
+		StringReader reader = new StringReader(json);
+		Object[] result = JSON.parse(reader, new Class<?>[] { Map.class });
+		Assert.assertEquals(1, result.length);
+		Assert.assertEquals("bbb", ((Map<String, String>)result[0]).get("aaa"));
+	}
+
+	@SuppressWarnings("unchecked")
+	public void testLinkedMap() throws Exception {
+		LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
+		map.put("aaa", "bbb");
+		
+		StringWriter writer = new StringWriter();
+		JSON.json(map, writer);
+		String json = writer.getBuffer().toString();
+		Assert.assertEquals("{\"aaa\":\"bbb\"}", json);
+
+		StringReader reader = new StringReader(json);
+		LinkedHashMap<String, String> result = JSON.parse(reader, LinkedHashMap.class);
+		Assert.assertEquals("bbb", result.get("aaa"));
+	}
+
+	public void testObject2Json() throws Exception
+	{
+		Bean bean = new Bean();
+		bean.array = new int[]{1, 3, 4};
+		bean.setName("ql");
+
+		String json = JSON.json(bean);
+		bean = JSON.parse(json, Bean.class);
+		assertEquals(bean.getName(), "ql");
+		assertEquals(bean.getDisplayName(), "钱磊");
+		assertEquals(bean.bytes.length, DEFAULT_BYTES.length);
+		assertEquals(bean.$$, DEFAULT_$$);
+
+		assertEquals("{\"name\":\"ql\",\"array\":[1,3,4]}", JSON.json(bean, new String[]{"name", "array"}));
+	}
+
+	public void testParse2JSONObject() throws Exception
+	{
+		JSONObject jo = (JSONObject)JSON.parse("{name:'qianlei',array:[1,2,3,4,98.123],b1:TRUE,$1:NULL,$2:FALSE,__3:NULL}");
+		assertEquals(jo.getString("name"), "qianlei");
+		assertEquals(jo.getArray("array").length(), 5);
+		assertEquals(jo.get("$2"), Boolean.FALSE);
+		assertEquals(jo.get("__3"), null);
+
+		for(int i=0;i<10000;i++)
+			JSON.parse("{\"name\":\"qianlei\",\"array\":[1,2,3,4,98.123],\"displayName\":\"钱磊\"}");
+
+		long now = System.currentTimeMillis();
+		for(int i=0;i<10000;i++)
+			JSON.parse("{\"name\":\"qianlei\",\"array\":[1,2,3,4,98.123],\"displayName\":\"钱磊\"}");
+		System.out.println("parse to JSONObject 10000 times in: " + ( System.currentTimeMillis()-now) );
+	}
+
+	@SuppressWarnings("unchecked")
+	public void testParse2Class() throws Exception
+	{
+		int[] o1 = {1,2,3,4,5}, o2 = JSON.parse("[1.2,2,3,4,5]", int[].class);
+		assertEquals(o2.length, 5);
+		for(int i=0;i<5;i++)
+			assertEquals(o1[i], o2[i]);
+
+		List l1 = (List)JSON.parse("[1.2,2,3,4,5]", List.class);
+		assertEquals(l1.size(), 5);
+		for(int i=0;i<5;i++)
+			assertEquals(o1[i], ((Number)l1.get(i)).intValue());
+
+		Bean bean = JSON.parse("{name:'qianlei',array:[1,2,3,4,98.123],displayName:'钱磊',$$:214726,$b:TRUE}", Bean.class);
+		assertEquals(bean.getName(), "qianlei");
+		assertEquals(bean.getDisplayName(), "钱磊");
+		assertEquals(bean.array.length, 5);
+		assertEquals(bean.$$, 214726);
+		assertEquals(bean.$b, true);
+
+		for(int i=0;i<10000;i++)
+			JSON.parse("{name:'qianlei',array:[1,2,3,4,98.123],displayName:'钱磊'}", Bean1.class);
+	
+		long now = System.currentTimeMillis();
+		for(int i=0;i<10000;i++)
+			JSON.parse("{name:'qianlei',array:[1,2,3,4,98.123],displayName:'钱磊'}", Bean1.class);
+		System.out.println("parse to Class 10000 times in: " + ( System.currentTimeMillis()-now) );
+	}
+
+	public void testParse2Arguments() throws Exception
+	{
+		Object[] test = JSON.parse("[1.2, 2, {name:'qianlei',array:[1,2,3,4,98.123]} ]", new Class<?>[]{ int.class, int.class, Bean.class });
+		assertEquals(test[1], 2);
+		assertEquals(test[2].getClass(), Bean.class);
+		test = JSON.parse("[1.2, 2]", new Class<?>[]{ int.class, int.class });
+		assertEquals(test[0], 1);
+	}
+
+	static byte[] DEFAULT_BYTES = {3,12,14,41,12,2,3,12,4,67,23};
+
+	static int DEFAULT_$$ = 152;
+
+	public static class Bean1
+	{
+		private String name,displayName;
+
+		public int[] array;
+
+		public String getDisplayName() {
+			return displayName;
+		}
+
+		public void setDisplayName(String displayName) {
+			this.displayName = displayName;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			this.name = name;
+		}
+	}
+
+	public static class Bean
+	{
+		private String name, displayName = "钱磊";
+
+		public int[] array;
+
+		public boolean $b;
+
+		public int $$ = DEFAULT_$$;
+
+		public byte[] bytes = DEFAULT_BYTES;
+
+		public String getDisplayName() {
+			return displayName;
+		}
+
+		public void setDisplayName(String displayName) {
+			this.displayName = displayName;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			this.name = name;
+		}
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONWriterTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONWriterTest.java
new file mode 100644
index 0000000..16a71e8
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONWriterTest.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;
+
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+public class JSONWriterTest extends TestCase
+{
+	public void testWriteJson() throws Exception
+	{
+		StringWriter w = new StringWriter();
+		JSONWriter writer = new JSONWriter(w);
+
+		writer.valueNull();
+		assertEquals(w.getBuffer().toString(), "null");
+
+		// write array.
+		w.getBuffer().setLength(0);
+		writer.arrayBegin().valueNull().valueBoolean(false).valueInt(16).arrayEnd();
+		assertEquals(w.getBuffer().toString(),"[null,false,16]");
+
+		// write object.
+		w.getBuffer().setLength(0);
+		writer.objectBegin().objectItem("type").valueString("com.alibaba.dubbo.TestService").objectItem("version").valueString("1.1.0").objectEnd();
+		assertEquals(w.getBuffer().toString(),"{\"type\":\"com.alibaba.dubbo.TestService\",\"version\":\"1.1.0\"}");
+
+		w.getBuffer().setLength(0);
+		writer.objectBegin();
+		writer.objectItem("name").objectItem("displayName");
+		writer.objectItem("emptyList").arrayBegin().arrayEnd();
+		writer.objectItem("list").arrayBegin().valueNull().valueBoolean(false).valueInt(16).valueString("stri'''ng").arrayEnd();
+		writer.objectItem("service").objectBegin().objectItem("type").valueString("com.alibaba.dubbo.TestService").objectItem("version").valueString("1.1.0").objectEnd();
+		writer.objectEnd();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/MyException.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/MyException.java
new file mode 100644
index 0000000..178916d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/MyException.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.json;

+

+public class MyException extends Exception {

+

+	private static final long serialVersionUID = 2905707783883694687L;

+	

+	private String code;

+	

+	public MyException() {

+	}

+

+	public MyException(String code, String message) {

+		super(message);

+		this.code = code;

+	}

+

+	public String getCode() {

+		return code;

+	}

+

+	public void setCode(String code) {

+		this.code = code;

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/AnimalEnum.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/AnimalEnum.java
new file mode 100644
index 0000000..61ca153
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/AnimalEnum.java
@@ -0,0 +1,24 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model;
+
+/**
+ * @author ding.lid
+ *
+ */
+public enum AnimalEnum {
+    dog, cat, rat, cow, bull, horse;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizException.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizException.java
new file mode 100644
index 0000000..f2bf7f8
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizException.java
@@ -0,0 +1,28 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model;
+
+public class BizException extends RuntimeException{
+    
+    private static final long serialVersionUID = 1L;
+    
+    public BizException(String message){
+        super(message);
+    }
+    
+    public BizException() {
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizExceptionNoDefaultConstructor.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizExceptionNoDefaultConstructor.java
new file mode 100644
index 0000000..92ae730
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizExceptionNoDefaultConstructor.java
@@ -0,0 +1,25 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model;
+
+public class BizExceptionNoDefaultConstructor extends RuntimeException{
+    
+    private static final long serialVersionUID = 1L;
+    
+    public BizExceptionNoDefaultConstructor(String message){
+        super(message);
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/Person.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/Person.java
new file mode 100644
index 0000000..85ae64d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/Person.java
@@ -0,0 +1,99 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model;
+
+import java.util.Arrays;
+
+/**
+ * @author ding.lid
+ */
+public class Person {
+    private String name = "name1";
+    
+    byte oneByte = 123;
+    
+    private int age = 11;
+    
+    private String[] value = {"value1", "value2"};
+    
+    public String getName() {
+        return name;
+    }
+
+    public byte getOneByte() {
+        return oneByte;
+    }
+
+    public void setOneByte(byte b) {
+        this.oneByte = b;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public String[] getValue() {
+        return value;
+    }
+
+    public void setValue(String[] value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + age;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + Arrays.hashCode(value);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Person other = (Person) obj;
+        if (age != other.age)
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        if (!Arrays.equals(value, other.value))
+            return false;
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/SerializablePerson.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/SerializablePerson.java
new file mode 100644
index 0000000..a03b901
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/SerializablePerson.java
@@ -0,0 +1,102 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * @author ding.lid
+ */
+public class SerializablePerson implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String name = "name1";
+    
+    byte oneByte = 123;
+    
+    private int age = 11;
+    
+    private String[] value = {"value1", "value2"};
+    
+    public String getName() {
+        return name;
+    }
+
+    public byte getOneByte() {
+        return oneByte;
+    }
+
+    public void setOneByte(byte b) {
+        this.oneByte = b;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public String[] getValue() {
+        return value;
+    }
+
+    public void setValue(String[] value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + age;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + Arrays.hashCode(value);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        SerializablePerson other = (SerializablePerson) obj;
+        if (age != other.age)
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        if (!Arrays.equals(value, other.value))
+            return false;
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Image.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Image.java
new file mode 100644
index 0000000..478eb90
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Image.java
@@ -0,0 +1,123 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model.media;
+
+
+public class Image implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    public enum Size {
+		SMALL, LARGE
+	}
+
+	public String uri;
+
+	public String title;  // Can be null
+	public int width;
+	public int height;
+	public Size size;
+
+	public Image() {}
+
+	public Image (String uri, String title, int width, int height, Size size) {
+		this.height = height;
+		this.title = title;
+		this.uri = uri;
+		this.width = width;
+		this.size = size;
+	}
+
+	@Override
+	public boolean equals(Object o)
+	{
+		if (this == o) return true;
+		if (o == null || getClass() != o.getClass()) return false;
+
+		Image image = (Image) o;
+
+		if (height != image.height) return false;
+		if (width != image.width) return false;
+		if (size != image.size) return false;
+		if (title != null ? !title.equals(image.title) : image.title != null) return false;
+		if (uri != null ? !uri.equals(image.uri) : image.uri != null) return false;
+
+		return true;
+	}
+
+	@Override
+	public int hashCode()
+	{
+		int result = uri != null ? uri.hashCode() : 0;
+		result = 31 * result + (title != null ? title.hashCode() : 0);
+		result = 31 * result + width;
+		result = 31 * result + height;
+		result = 31 * result + (size != null ? size.hashCode() : 0);
+		return result;
+	}
+
+	public String toString () {
+		StringBuilder sb = new StringBuilder();
+		sb.append("[Image ");
+		sb.append("uri=").append(uri);
+		sb.append(", title=").append(title);
+		sb.append(", width=").append(width);
+		sb.append(", height=").append(height);
+		sb.append(", size=").append(size);
+		sb.append("]");
+		return sb.toString();
+	}
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public void setSize(Size size) {
+        this.size = size;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public Size getSize() {
+        return size;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Media.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Media.java
new file mode 100644
index 0000000..a3683bb
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Media.java
@@ -0,0 +1,208 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model.media;
+
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class Media implements java.io.Serializable {
+	public enum Player {
+		JAVA, FLASH
+	}
+
+	public String uri;
+	public String title;        // Can be unset.
+	public int width;
+	public int height;
+	public String format;
+	public long duration;
+	public long size;
+	public int bitrate;         // Can be unset.
+	public boolean hasBitrate;
+	public List<String> persons;
+	
+	public Player player;
+
+	public String copyright;    // Can be unset.
+
+	public Media() {}
+
+	public Media(String uri, String title, int width, int height, String format, long duration, long size, int bitrate, boolean hasBitrate, List<String> persons, Player player, String copyright)
+	{
+		this.uri = uri;
+		this.title = title;
+		this.width = width;
+		this.height = height;
+		this.format = format;
+		this.duration = duration;
+		this.size = size;
+		this.bitrate = bitrate;
+		this.hasBitrate = hasBitrate;
+		this.persons = persons;
+		this.player = player;
+		this.copyright = copyright;
+	}
+
+	@Override
+	public boolean equals(Object o)
+	{
+		if (this == o) return true;
+		if (o == null || getClass() != o.getClass()) return false;
+
+		Media media = (Media) o;
+
+		if (bitrate != media.bitrate) return false;
+		if (duration != media.duration) return false;
+		if (hasBitrate != media.hasBitrate) return false;
+		if (height != media.height) return false;
+		if (size != media.size) return false;
+		if (width != media.width) return false;
+		if (copyright != null ? !copyright.equals(media.copyright) : media.copyright != null) return false;
+		if (format != null ? !format.equals(media.format) : media.format != null) return false;
+		if (persons != null ? !persons.equals(media.persons) : media.persons != null) return false;
+		if (player != media.player) return false;
+		if (title != null ? !title.equals(media.title) : media.title != null) return false;
+		if (uri != null ? !uri.equals(media.uri) : media.uri != null) return false;
+
+		return true;
+	}
+
+	@Override
+	public int hashCode()
+	{
+		int result = uri != null ? uri.hashCode() : 0;
+		result = 31 * result + (title != null ? title.hashCode() : 0);
+		result = 31 * result + width;
+		result = 31 * result + height;
+		result = 31 * result + (format != null ? format.hashCode() : 0);
+		result = 31 * result + (int) (duration ^ (duration >>> 32));
+		result = 31 * result + (int) (size ^ (size >>> 32));
+		result = 31 * result + bitrate;
+		result = 31 * result + (hasBitrate ? 1 : 0);
+		result = 31 * result + (persons != null ? persons.hashCode() : 0);
+		result = 31 * result + (player != null ? player.hashCode() : 0);
+		result = 31 * result + (copyright != null ? copyright.hashCode() : 0);
+		return result;
+	}
+
+	public String toString () {
+		StringBuilder sb = new StringBuilder();
+		sb.append("[Media ");
+		sb.append("uri=").append(uri);
+		sb.append(", title=").append(title);
+		sb.append(", width=").append(width);
+		sb.append(", height=").append(height);
+		sb.append(", format=").append(format);
+		sb.append(", duration=").append(duration);
+		sb.append(", size=").append(size);
+		sb.append(", hasBitrate=").append(hasBitrate);
+		sb.append(", bitrate=").append(String.valueOf(bitrate));
+		sb.append(", persons=").append(persons);
+		sb.append(", player=").append(player);
+		sb.append(", copyright=").append(copyright);
+		sb.append("]");
+		return sb.toString();
+	}
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public void setFormat(String format) {
+        this.format = format;
+    }
+
+    public void setDuration(long duration) {
+        this.duration = duration;
+    }
+
+    public void setSize(long size) {
+        this.size = size;
+    }
+
+    public void setBitrate(int bitrate) {
+        this.bitrate = bitrate;
+        this.hasBitrate = true;
+    }
+
+    public void setPersons(List<String> persons) {
+        this.persons = persons;
+    }
+
+    public void setPlayer(Player player) {
+        this.player = player;
+    }
+
+    public void setCopyright(String copyright) {
+        this.copyright = copyright;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public String getFormat() {
+        return format;
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    public long getSize() {
+        return size;
+    }
+
+    public int getBitrate() {
+        return bitrate;
+    }
+
+    public List<String> getPersons() {
+        return persons;
+    }
+
+    public Player getPlayer() {
+        return player;
+    }
+
+    public String getCopyright() {
+        return copyright;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/MediaContent.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/MediaContent.java
new file mode 100644
index 0000000..3a8d9e6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/MediaContent.java
@@ -0,0 +1,79 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model.media;
+
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class MediaContent implements java.io.Serializable
+{
+	public Media media;
+	public List<Image> images;
+
+	public MediaContent() {}
+
+	public MediaContent (Media media, List<Image> images) {
+		this.media = media;
+		this.images = images;
+	}
+
+	@Override
+	public boolean equals(Object o)
+	{
+		if (this == o) return true;
+		if (o == null || getClass() != o.getClass()) return false;
+
+		MediaContent that = (MediaContent) o;
+
+		if (images != null ? !images.equals(that.images) : that.images != null) return false;
+		if (media != null ? !media.equals(that.media) : that.media != null) return false;
+
+		return true;
+	}
+
+	@Override
+	public int hashCode()
+	{
+		int result = media != null ? media.hashCode() : 0;
+		result = 31 * result + (images != null ? images.hashCode() : 0);
+		return result;
+	}
+
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("[MediaContent: ");
+		sb.append("media=").append(media);
+		sb.append(", images=").append(images);
+		sb.append("]");
+		return sb.toString();
+	}
+
+    public void setMedia(Media media) {
+        this.media = media;
+    }
+
+    public void setImages(List<Image> images) {
+        this.images = images;
+    }
+
+    public Media getMedia() {
+        return media;
+    }
+
+    public List<Image> getImages() {
+        return images;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/BigPerson.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/BigPerson.java
new file mode 100644
index 0000000..5165232
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/BigPerson.java
@@ -0,0 +1,153 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model.person;
+
+import java.io.Serializable;
+
+/**
+ * @author tony.chenl
+ */
+public class BigPerson implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    String                    personId;
+
+    String                    loginName;
+
+    PersonStatus              status;
+
+    String                    email;
+
+    String                    penName;
+
+    PersonInfo                infoProfile;
+
+    public BigPerson() {
+
+    }
+
+    public BigPerson(String id) {
+        this.personId = id;
+    }
+
+    public String getPersonId() {
+        return personId;
+    }
+
+    public void setPersonId(String personId) {
+        this.personId = personId;
+    }
+
+    public PersonInfo getInfoProfile() {
+        return infoProfile;
+    }
+
+    public void setInfoProfile(PersonInfo infoProfile) {
+        this.infoProfile = infoProfile;
+    }
+
+    public void setLoginName(String loginName) {
+        this.loginName = loginName;
+    }
+
+    public void setStatus(PersonStatus status) {
+        this.status = status;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public void setPenName(String penName) {
+        this.penName = penName;
+    }
+
+    public String getEmail() {
+        return this.email;
+    }
+
+    public String getLoginName() {
+        return this.loginName;
+    }
+
+    public PersonStatus getStatus() {
+        return this.status;
+    }
+
+    public String getPenName() {
+        return penName;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((email == null) ? 0 : email.hashCode());
+        result = prime * result + ((infoProfile == null) ? 0 : infoProfile.hashCode());
+        result = prime * result + ((loginName == null) ? 0 : loginName.hashCode());
+        result = prime * result + ((penName == null) ? 0 : penName.hashCode());
+        result = prime * result + ((personId == null) ? 0 : personId.hashCode());
+        result = prime * result + ((status == null) ? 0 : status.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BigPerson other = (BigPerson) obj;
+        if (email == null) {
+            if (other.email != null)
+                return false;
+        } else if (!email.equals(other.email))
+            return false;
+        if (infoProfile == null) {
+            if (other.infoProfile != null)
+                return false;
+        } else if (!infoProfile.equals(other.infoProfile))
+            return false;
+        if (loginName == null) {
+            if (other.loginName != null)
+                return false;
+        } else if (!loginName.equals(other.loginName))
+            return false;
+        if (penName == null) {
+            if (other.penName != null)
+                return false;
+        } else if (!penName.equals(other.penName))
+            return false;
+        if (personId == null) {
+            if (other.personId != null)
+                return false;
+        } else if (!personId.equals(other.personId))
+            return false;
+        if (status != other.status)
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "BigPerson [personId=" + personId + ", loginName=" + loginName + ", status="
+                + status + ", email=" + email + ", penName=" + penName + ", infoProfile="
+                + infoProfile + "]";
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/FullAddress.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/FullAddress.java
new file mode 100644
index 0000000..13d0d53
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/FullAddress.java
@@ -0,0 +1,204 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model.person;
+
+import java.io.Serializable;
+
+/**
+ * @author xk1430
+ */
+public class FullAddress implements Serializable {
+
+    private static final long serialVersionUID = 5163979984269419831L;
+
+    private String            countryId;
+
+    private String            countryName;
+
+    private String            provinceName;
+
+    private String            cityId;
+
+    private String            cityName;
+
+    private String            streetAddress;
+
+    private String            zipCode;
+
+    public void setCountryId(String countryId) {
+        this.countryId = countryId;
+    }
+
+    public void setCountryName(String countryName) {
+        this.countryName = countryName;
+    }
+
+    public void setProvinceName(String provinceName) {
+        this.provinceName = provinceName;
+    }
+
+    public void setCityId(String cityId) {
+        this.cityId = cityId;
+    }
+
+    public void setCityName(String cityName) {
+        this.cityName = cityName;
+    }
+
+    public void setStreetAddress(String streetAddress) {
+        this.streetAddress = streetAddress;
+    }
+
+    public void setZipCode(String zipCode) {
+        this.zipCode = zipCode;
+    }
+
+    public String getCountryId() {
+        return countryId;
+    }
+
+    public String getCountryName() {
+        return countryName;
+    }
+
+    public String getProvinceName() {
+        return provinceName;
+    }
+
+    public String getCityId() {
+        return cityId;
+    }
+
+    public String getCityName() {
+        return cityName;
+    }
+
+    public String getStreetAddress() {
+        return streetAddress;
+    }
+
+    public String getZipCode() {
+        return zipCode;
+    }
+
+    public FullAddress() {
+    }
+
+    public FullAddress(String countryId, String provinceName, String cityId, String streetAddress,
+                       String zipCode) {
+        this.countryId = countryId;
+        this.countryName = countryId;
+        this.provinceName = provinceName;
+        this.cityId = cityId;
+        this.cityName = cityId;
+        this.streetAddress = streetAddress;
+        this.zipCode = zipCode;
+    }
+
+    public FullAddress(String countryId, String countryName, String provinceName, String cityId,
+                       String cityName, String streetAddress, String zipCode) {
+        this.countryId = countryId;
+        this.countryName = countryName;
+        this.provinceName = provinceName;
+        this.cityId = cityId;
+        this.cityName = cityName;
+        this.streetAddress = streetAddress;
+        this.zipCode = zipCode;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((cityId == null) ? 0 : cityId.hashCode());
+        result = prime * result + ((cityName == null) ? 0 : cityName.hashCode());
+        result = prime * result + ((countryId == null) ? 0 : countryId.hashCode());
+        result = prime * result + ((countryName == null) ? 0 : countryName.hashCode());
+        result = prime * result + ((provinceName == null) ? 0 : provinceName.hashCode());
+        result = prime * result + ((streetAddress == null) ? 0 : streetAddress.hashCode());
+        result = prime * result + ((zipCode == null) ? 0 : zipCode.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        FullAddress other = (FullAddress) obj;
+        if (cityId == null) {
+            if (other.cityId != null)
+                return false;
+        } else if (!cityId.equals(other.cityId))
+            return false;
+        if (cityName == null) {
+            if (other.cityName != null)
+                return false;
+        } else if (!cityName.equals(other.cityName))
+            return false;
+        if (countryId == null) {
+            if (other.countryId != null)
+                return false;
+        } else if (!countryId.equals(other.countryId))
+            return false;
+        if (countryName == null) {
+            if (other.countryName != null)
+                return false;
+        } else if (!countryName.equals(other.countryName))
+            return false;
+        if (provinceName == null) {
+            if (other.provinceName != null)
+                return false;
+        } else if (!provinceName.equals(other.provinceName))
+            return false;
+        if (streetAddress == null) {
+            if (other.streetAddress != null)
+                return false;
+        } else if (!streetAddress.equals(other.streetAddress))
+            return false;
+        if (zipCode == null) {
+            if (other.zipCode != null)
+                return false;
+        } else if (!zipCode.equals(other.zipCode))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (countryName != null && countryName.length() > 0) {
+            sb.append(countryName);
+        }
+        if (provinceName != null && provinceName.length() > 0) {
+            sb.append(" ");
+            sb.append(provinceName);
+        }
+        if (cityName != null && cityName.length() > 0) {
+            sb.append(" ");
+            sb.append(cityName);
+        }
+        if (streetAddress != null && streetAddress.length() > 0) {
+            sb.append(" ");
+            sb.append(streetAddress);
+        }
+        return sb.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonInfo.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonInfo.java
new file mode 100644
index 0000000..2c48441
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonInfo.java
@@ -0,0 +1,208 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model.person;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author tony.chenl
+ */
+public class PersonInfo implements Serializable {
+    private static final long serialVersionUID = 7443011149612231882L;
+
+    List<Phone>               phones;
+
+    Phone                     fax;
+
+    FullAddress               fullAddress;
+
+    String                    mobileNo;
+
+    String                    name;
+
+    boolean                   male;
+
+    boolean                   female;
+
+    String                    department;
+
+    String                    jobTitle;
+
+    String                    homepageUrl;
+
+    public List<Phone> getPhones() {
+        return phones;
+    }
+
+    public void setPhones(List<Phone> phones) {
+        this.phones = phones;
+    }
+
+    public boolean isMale() {
+        return male;
+    }
+
+    public void setMale(boolean male) {
+        this.male = male;
+    }
+
+    public boolean isFemale() {
+        return female;
+    }
+
+    public void setFemale(boolean female) {
+        this.female = female;
+    }
+
+    public void setFax(Phone fax) {
+        this.fax = fax;
+    }
+
+    public void setFullAddress(FullAddress fullAddress) {
+        this.fullAddress = fullAddress;
+    }
+
+    public void setMobileNo(String mobileNo) {
+        this.mobileNo = mobileNo;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setDepartment(String department) {
+        this.department = department;
+    }
+
+    public void setJobTitle(String jobTitle) {
+        this.jobTitle = jobTitle;
+    }
+
+    public void setHomepageUrl(String homepageUrl) {
+        this.homepageUrl = homepageUrl;
+    }
+
+    public String getDepartment() {
+        return department;
+    }
+
+    public Phone getFax() {
+        return fax;
+    }
+
+    public FullAddress getFullAddress() {
+        return fullAddress;
+    }
+
+    public String getHomepageUrl() {
+        return homepageUrl;
+    }
+
+    public String getJobTitle() {
+        return jobTitle;
+    }
+
+    public String getMobileNo() {
+        return mobileNo;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((department == null) ? 0 : department.hashCode());
+        result = prime * result + ((fax == null) ? 0 : fax.hashCode());
+        result = prime * result + (female ? 1231 : 1237);
+        result = prime * result + ((fullAddress == null) ? 0 : fullAddress.hashCode());
+        result = prime * result + ((homepageUrl == null) ? 0 : homepageUrl.hashCode());
+        result = prime * result + ((jobTitle == null) ? 0 : jobTitle.hashCode());
+        result = prime * result + (male ? 1231 : 1237);
+        result = prime * result + ((mobileNo == null) ? 0 : mobileNo.hashCode());
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + ((phones == null) ? 0 : phones.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        PersonInfo other = (PersonInfo) obj;
+        if (department == null) {
+            if (other.department != null)
+                return false;
+        } else if (!department.equals(other.department))
+            return false;
+        if (fax == null) {
+            if (other.fax != null)
+                return false;
+        } else if (!fax.equals(other.fax))
+            return false;
+        if (female != other.female)
+            return false;
+        if (fullAddress == null) {
+            if (other.fullAddress != null)
+                return false;
+        } else if (!fullAddress.equals(other.fullAddress))
+            return false;
+        if (homepageUrl == null) {
+            if (other.homepageUrl != null)
+                return false;
+        } else if (!homepageUrl.equals(other.homepageUrl))
+            return false;
+        if (jobTitle == null) {
+            if (other.jobTitle != null)
+                return false;
+        } else if (!jobTitle.equals(other.jobTitle))
+            return false;
+        if (male != other.male)
+            return false;
+        if (mobileNo == null) {
+            if (other.mobileNo != null)
+                return false;
+        } else if (!mobileNo.equals(other.mobileNo))
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        if (phones == null) {
+            if (other.phones != null)
+                return false;
+        } else if (!phones.equals(other.phones))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "PersonInfo [phones=" + phones + ", fax=" + fax + ", fullAddress=" + fullAddress
+                + ", mobileNo=" + mobileNo + ", name=" + name + ", male=" + male + ", female="
+                + female + ", department=" + department + ", jobTitle=" + jobTitle
+                + ", homepageUrl=" + homepageUrl + "]";
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonStatus.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonStatus.java
new file mode 100644
index 0000000..75a4d58
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonStatus.java
@@ -0,0 +1,24 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model.person;
+
+/**
+ * @author tony.chenl
+ */
+public enum PersonStatus {
+    ENABLED,
+    DISABLED
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/Phone.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/Phone.java
new file mode 100644
index 0000000..8ee36d5
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/Phone.java
@@ -0,0 +1,143 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.model.person;
+
+import java.io.Serializable;
+
+/**
+ * 电话号码 
+ * 
+ * @author xk1430
+ */
+public class Phone implements Serializable {
+
+    private static final long serialVersionUID = 4399060521859707703L;
+
+    private String            country;
+
+    private String            area;
+
+    private String            number;
+
+    private String            extensionNumber;
+
+    public Phone() {
+    }
+
+    public Phone(String country, String area, String number, String extensionNumber) {
+        this.country = country;
+        this.area = area;
+        this.number = number;
+        this.extensionNumber = extensionNumber;
+    }
+
+    public void setCountry(String country) {
+        this.country = country;
+    }
+
+    public void setArea(String area) {
+        this.area = area;
+    }
+
+    public void setNumber(String number) {
+        this.number = number;
+    }
+
+    public void setExtensionNumber(String extensionNumber) {
+        this.extensionNumber = extensionNumber;
+    }
+
+    public String getCountry() {
+        return country;
+    }
+
+    public String getArea() {
+        return area;
+    }
+
+    public String getNumber() {
+        return number;
+    }
+
+    public String getExtensionNumber() {
+        return extensionNumber;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((area == null) ? 0 : area.hashCode());
+        result = prime * result + ((country == null) ? 0 : country.hashCode());
+        result = prime * result + ((extensionNumber == null) ? 0 : extensionNumber.hashCode());
+        result = prime * result + ((number == null) ? 0 : number.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Phone other = (Phone) obj;
+        if (area == null) {
+            if (other.area != null)
+                return false;
+        } else if (!area.equals(other.area))
+            return false;
+        if (country == null) {
+            if (other.country != null)
+                return false;
+        } else if (!country.equals(other.country))
+            return false;
+        if (extensionNumber == null) {
+            if (other.extensionNumber != null)
+                return false;
+        } else if (!extensionNumber.equals(other.extensionNumber))
+            return false;
+        if (number == null) {
+            if (other.number != null)
+                return false;
+        } else if (!number.equals(other.number))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (country != null && country.length() > 0) {
+            sb.append(country);
+            sb.append("-");
+        }
+        if (area != null && area.length() > 0) {
+            sb.append(area);
+            sb.append("-");
+        }
+        if (number != null && number.length() > 0) {
+            sb.append(number);
+        }
+        if (extensionNumber != null && extensionNumber.length() > 0) {
+            sb.append("-");
+            sb.append(extensionNumber);
+        }
+        return sb.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/SerializationCompareTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/SerializationCompareTest.java
new file mode 100644
index 0000000..a0bb1ea
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/SerializationCompareTest.java
@@ -0,0 +1,231 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize;
+
+import static org.junit.Assert.assertEquals;

+

+import java.io.ByteArrayInputStream;

+import java.io.ByteArrayOutputStream;

+import java.io.ObjectInputStream;

+import java.io.ObjectOutputStream;

+import java.io.Serializable;

+import java.util.ArrayList;

+import java.util.HashMap;

+

+import org.junit.Test;

+

+import com.alibaba.com.caucho.hessian.io.Hessian2Input;

+import com.alibaba.com.caucho.hessian.io.Hessian2Output;

+import com.alibaba.dubbo.common.io.Bytes;

+import com.alibaba.dubbo.common.serialize.support.dubbo.Builder;

+import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectInputStream;

+import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectOutputStream;

+

+/**

+ * @author qian.lei

+ * @author ding.lid

+ */
+public class SerializationCompareTest
+{
+	@Test
+	public void test_CompareSerializeLength() throws Exception
+	{
+		long[] data = new long[]{ -1l, 2l, 3l, 4l, 5l };
+		ByteArrayOutputStream os;
+
+		os = new ByteArrayOutputStream();
+		ObjectOutputStream jos = new ObjectOutputStream(os);
+		jos.writeObject(data);
+		System.out.println("java:"+Bytes.bytes2hex(os.toByteArray())+":"+os.size());
+
+		os = new ByteArrayOutputStream();
+		CompactedObjectOutputStream oos = new CompactedObjectOutputStream(os);
+		oos.writeObject(data);
+		System.out.println("compacted java:"+Bytes.bytes2hex(os.toByteArray())+":"+os.size());
+
+		os = new ByteArrayOutputStream();
+		Hessian2Output h2o = new Hessian2Output(os);
+		h2o.writeObject(data);
+		h2o.flushBuffer();
+		System.out.println("hessian:"+Bytes.bytes2hex(os.toByteArray())+":"+os.size());
+
+		os = new ByteArrayOutputStream();
+		Builder<long[]> lb = Builder.register(long[].class);
+		lb.writeTo(data, os);
+		System.out.println("DataOutput:"+Bytes.bytes2hex(os.toByteArray())+":"+os.size());
+	}
+

+	@Test
+	public void testBuilderPerm() throws Exception
+	{
+		Builder<Bean> bb = Builder.register(Bean.class);
+		Bean bean = new Bean();
+		int len = 0;
+		long now = System.currentTimeMillis();
+		for(int i=0;i<500;i++)
+		{
+			ByteArrayOutputStream os = new ByteArrayOutputStream();
+			bb.writeTo(bean, os);
+			os.close();
+			if( i == 0 )
+				len = os.toByteArray().length;

+			
+			ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());

+			Bean b = bb.parseFrom(is);
+			assertEquals(b.getClass(), Bean.class);
+		}
+		System.out.println("Builder write and parse 500 times in " + (System.currentTimeMillis()-now)+"ms, size " + len);
+	}
+

+	@Test
+	public void testH2oPerm() throws Exception
+	{
+		Bean bean = new Bean();
+		int len = 0;
+		long now = System.currentTimeMillis();
+		for(int i=0;i<500;i++)
+		{
+			ByteArrayOutputStream os = new ByteArrayOutputStream();
+			Hessian2Output out = new Hessian2Output(os);
+			out.writeObject(bean);
+			out.flushBuffer();
+			os.close();
+			if( i == 0 )
+				len = os.toByteArray().length;
+			ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
+			Hessian2Input in = new Hessian2Input(is);
+			assertEquals(in.readObject().getClass(), Bean.class);
+		}
+		System.out.println("Hessian2 write and parse 500 times in " + (System.currentTimeMillis()-now)+"ms, size " + len);
+	}
+

+	@Test
+	public void testJavaOutputPerm() throws Exception
+	{
+		Bean bean = new Bean();
+		int len = 0;
+		long now = System.currentTimeMillis();
+		for(int i=0;i<500;i++)
+		{
+			ByteArrayOutputStream os = new ByteArrayOutputStream();
+			ObjectOutputStream out = new ObjectOutputStream(os);
+			out.writeObject(bean);
+			os.close();
+			if( i == 0 )
+				len = os.toByteArray().length;
+			ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
+			ObjectInputStream in = new ObjectInputStream(is);
+			assertEquals(in.readObject().getClass(), Bean.class);
+		}
+		System.out.println("java write and parse 500 times in " + (System.currentTimeMillis()-now)+"ms, size " + len);
+	}
+

+	@Test
+	public void testCompactedJavaOutputPerm() throws Exception
+	{
+		Bean bean = new Bean();
+		int len = 0;
+		long now = System.currentTimeMillis();
+		for(int i=0;i<500;i++)
+		{
+			ByteArrayOutputStream os = new ByteArrayOutputStream();
+			CompactedObjectOutputStream out = new CompactedObjectOutputStream(os);
+			out.writeObject(bean);
+			os.close();
+			if( i == 0 )
+				len = os.toByteArray().length;
+			ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
+			CompactedObjectInputStream in = new CompactedObjectInputStream(is);
+			assertEquals(in.readObject().getClass(), Bean.class);
+		}
+		System.out.println("compacted java write and parse 500 times in " + (System.currentTimeMillis()-now)+"ms, size " + len);
+	}
+
+	public static enum EnumTest { READ, WRITE, CREATE, UNREGISTER };
+
+	static class MyList<T> extends ArrayList<T>
+	{
+        private static final long serialVersionUID = 1L;

+        

+        private int code = 12345;
+		private String id = "feedback";
+	}
+
+	static class MyMap<K, V> extends HashMap<K, V>
+	{
+        private static final long serialVersionUID = 1L;

+        

+        private int code = 12345;
+		private String id = "feedback";
+	}
+
+	public static class Bean implements Serializable
+	{
+		private static final long serialVersionUID = 7737610585231102146L;
+
+		public EnumTest ve = EnumTest.CREATE;
+
+		public int vi = 0;
+		public long vl = 100l;
+
+		boolean b = true;
+		boolean[] bs = {false, true};
+
+		String s1 = "1234567890";
+		String s2 = "1234567890一二三四五六七八九零";
+
+		int i = 123123, ni = -12344, is[] = {1,2,3,4,-1,-2,-3,-4};
+		short s = 12, ns = -76;
+		double d = 12.345, nd = -12.345;
+		long l = 1281447759383l, nl = -13445l;
+		private ArrayList<Object> mylist = new ArrayList<Object>();
+		{
+			mylist.add(1);
+			mylist.add("qianlei");
+			mylist.add("qianlei");
+			mylist.add("qianlei");
+			mylist.add("qianlei");
+		}
+		private HashMap<Object, Object> mymap = new HashMap<Object, Object>();
+		{
+			mymap.put(1,2);
+			mymap.put(2,"1234");
+			mymap.put("2345",12938.122);
+			mymap.put("2345",-1);
+			mymap.put("2345",-1.20);
+		}
+
+		public ArrayList<Object> getMylist()
+		{
+			return mylist;
+		}
+
+		public void setMylist(ArrayList<Object> list)
+		{
+			mylist = list;
+		}
+
+		public HashMap<Object, Object> getMymap()
+		{
+			return mymap;
+		}
+
+		public void setMymap(HashMap<Object, Object> map)
+		{
+			mymap = map;
+		}
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/BuilderTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/BuilderTest.java
new file mode 100644
index 0000000..a9b688d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/BuilderTest.java
@@ -0,0 +1,528 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.dubbo;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import java.io.Serializable;
+import java.lang.reflect.Modifier;

+import java.sql.Time;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.serialize.support.dubbo.Builder;
+
+public class BuilderTest
+{
+    @Test
+	public void testPrimaryTypeBuilder() throws Exception
+	{
+		System.out.println((new byte[2]).hashCode());
+		Builder<String> builder = Builder.register(String.class);
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+		String v = "123";
+		builder.writeTo(v, os);
+		byte[] b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+		v = builder.parseFrom(b);
+		builder.writeTo(v, os);
+		b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+	}
+
+    @Test
+	public void testEnumBuilder() throws Exception
+	{
+		Builder<Type> builder = Builder.register(Type.class);
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+		Type v = Type.High;
+		builder.writeTo(v, os);
+		byte[] b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+		v = builder.parseFrom(b);
+	}
+
+    @Test
+	public void testThrowableBuilder() throws Exception
+	{
+		Builder<Throwable> builder = Builder.register(Throwable.class);
+		Throwable th = new Throwable();
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+		builder.writeTo(th, os);
+		byte[] b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+
+		th = builder.parseFrom(b);
+	}
+
+    @Test
+	public void testArrayClassBuilder() throws Exception
+	{
+		UnsafeByteArrayOutputStream os;
+
+		byte[] b;
+
+		Builder<Object[]> osb = Builder.register(Object[].class);
+		os = new UnsafeByteArrayOutputStream();
+		osb.writeTo(new Object[]{ new String[0] }, os);
+		b = os.toByteArray();
+
+		Builder<long[]> lsb = Builder.register(long[].class);
+		os = new UnsafeByteArrayOutputStream();
+		lsb.writeTo(new long[]{ 1,121232,-3,4,-5,61321432413l }, os);
+		lsb.writeTo(new long[]{ 1,121232,-3,4,-5,61321432413l }, os);
+		lsb.writeTo(new long[]{ 1,2,3,12131314,123132313135l,-6 }, os);
+		b = os.toByteArray();
+		long[] ls = lsb.parseFrom(b);
+		assertEquals(ls.length, 6);
+
+		Builder<byte[]> bsb = Builder.register(byte[].class);
+		os = new UnsafeByteArrayOutputStream();
+		bsb.writeTo("i am a string.".getBytes(), os);
+		b = os.toByteArray();
+
+		Builder<int[][]> iisb = Builder.register(int[][].class);
+		os = new UnsafeByteArrayOutputStream();
+		iisb.writeTo(new int[][]{ {1,2,3,4}, {5,6,7,8}, {9,10}, {122,123,444} }, os);
+		b = os.toByteArray();
+		int[][] iis = iisb.parseFrom(b);
+		assertEquals(iis.length, 4);
+
+		Builder<int[][][]> iiisb = Builder.register(int[][][].class);
+		os = new UnsafeByteArrayOutputStream();
+		iiisb.writeTo(new int[][][]{ 
+			{{1,2,3,4}},
+			{{5,6,7,8}},
+			{{122,123,444}}
+		}, os);
+		b = os.toByteArray();
+		int[][][] iii = iiisb.parseFrom(b);
+		assertEquals(iii.length, 3);
+	}
+
+    @Test
+	public void testObjectBuilder() throws Exception
+	{
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+		Builder<Bean> BeanBuilder = Builder.register(Bean.class);
+
+		Bean bean = new Bean();
+		bean.name = "ql";
+		bean.type = Type.High;
+		bean.types = new Type[]{ Type.High, Type.High };
+		BeanBuilder.writeTo(bean, os);
+
+		byte[] b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+
+		bean = BeanBuilder.parseFrom(b);
+		assertNull(bean.time);
+		assertEquals(bean.i, 123123);
+		assertEquals(bean.ni, -12344);
+		assertEquals(bean.d, 12.345);
+		assertEquals(bean.nd, -12.345);
+		assertEquals(bean.l, 1281447759383l);
+		assertEquals(bean.nl, -13445l);
+		assertEquals(bean.vl, 100l);
+		assertEquals(bean.type, Type.High);
+		assertEquals(bean.types.length, 2);
+		assertEquals(bean.types[0], Type.High);
+		assertEquals(bean.types[1], Type.High);
+		assertEquals(bean.list.size(), 3);
+		assertEquals(bean.list.get(0), 1);
+		assertEquals(bean.list.get(1), 2);
+		assertEquals(bean.list.get(2), 1308147);
+	}
+
+    @Test
+	public void testInterfaceBuilder() throws Exception
+	{
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+		Builder<TestDO> builder = Builder.register(TestDO.class);
+		TestDO d = new TestDOImpl();
+		builder.writeTo(d, os);
+
+		byte[] b = os.toByteArray();
+
+		d = builder.parseFrom(b);
+		assertTrue(TestDO.class.isAssignableFrom(d.getClass()));
+		assertEquals("name", d.getName());
+		assertEquals(28, d.getArg());
+		assertEquals(Type.High, d.getType());
+	}
+
+    @Test
+	public void testGenericBuilder() throws Exception
+	{
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+		Builder<Object> ob = Builder.register(Object.class);
+
+		Object o = new Object();
+		ob.writeTo(o, os);
+		byte[] b = os.toByteArray();
+
+		os = new UnsafeByteArrayOutputStream();
+		Bean bean = new Bean();
+		bean.name = "ql";
+		bean.type = Type.High;
+		bean.types = new Type[]{ Type.High, Type.High };
+		ob.writeTo(bean, os);
+
+		b = os.toByteArray();
+		bean = (Bean)ob.parseFrom(b);
+		assertEquals(bean.i, 123123);
+		assertEquals(bean.ni, -12344);
+		assertEquals(bean.d, 12.345);
+		assertEquals(bean.nd, -12.345);
+		assertEquals(bean.l, 1281447759383l);
+		assertEquals(bean.nl, -13445l);
+		assertEquals(bean.vl, 100l);
+		assertEquals(bean.type, Type.High);
+		assertEquals(bean.types.length, 2);
+		assertEquals(bean.types[0], Type.High);
+		assertEquals(bean.types[1], Type.High);
+		assertEquals(bean.list.size(), 3);
+		assertEquals(bean.list.get(0), 1);
+		assertEquals(bean.list.get(1), 2);
+		assertEquals(bean.list.get(2), 1308147);
+	}
+
+    @Test
+	public void testObjectArrayBuilder() throws Exception
+	{
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+		Builder<Object[]> builder = Builder.register(Object[].class);
+
+		Object[] obj = new Object[5];
+		obj[0] = "1234";
+		obj[1] = new Double(109.23);
+		obj[2] = "3455";
+		obj[3] = null;
+		obj[4] = Boolean.TRUE;
+
+		builder.writeTo(obj, os);
+		byte[] b = os.toByteArray();
+		System.out.println("Object array:"+b.length+":"+Bytes.bytes2hex(b));
+
+		Assert.assertArrayEquals(obj, builder.parseFrom(b));
+	}
+
+    // FIXME MyList的从ArrayList中继承来的属性size会在decode时设置好,再Add时就不对了!!
+    @Ignore
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testBuilder_MyList() throws Exception
+	{
+        Builder<MyList> b1 = Builder.register(MyList.class);
+		MyList list = new MyList();
+		list.add(new boolean[]{ true,false });
+		list.add(new int[]{ 1,2,3,4,5 });
+		list.add("String");
+		list.add(4);
+		list.code = 4321;
+		
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+		b1.writeTo(list, os);
+		byte[] b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+		MyList result = b1.parseFrom(b);
+
+		assertEquals(4, result.size());
+		assertEquals(result.code, 4321);
+		assertEquals(result.id, "feedback");
+	}
+    
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testBuilder_MyMap() throws Exception
+    {
+        UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+        Builder<MyMap> b2 = Builder.register(MyMap.class);
+        MyMap map = new MyMap();
+        map.put("name", "qianlei");
+        map.put("displayName", "钱磊");
+        map.code = 4321;
+        b2.writeTo(map, os);
+        byte[] b = os.toByteArray();
+        System.out.println(b.length+":"+Bytes.bytes2hex(b));
+        
+        map = b2.parseFrom(b);
+        
+        assertEquals(map.size(), 2);
+        assertEquals(map.code, 4321);
+        assertEquals(map.id, "feedback");
+    }
+
+    @Test
+	@SuppressWarnings("unchecked")
+	public void testSerializableBean() throws Exception
+	{
+		System.out.println("testSerializableBean");
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+
+		SerializableBean sb = new SerializableBean();
+		Builder<SerializableBean> sbb = Builder.register(SerializableBean.class);
+		sbb.writeTo(sb, os);
+
+		byte[] b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+		assertEquals(sbb.parseFrom(os.toByteArray()), sb);
+	}
+
+    @Test
+	@SuppressWarnings("unchecked")
+	public void testOthers() throws Exception
+	{
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+
+		StringBuffer buf = new StringBuffer();
+		for(int i=0;i<1024*32+32;i++)
+			buf.append('A');
+		Builder<String> sb = Builder.register(String.class);
+		sb.writeTo(buf.toString(), os);
+		assertEquals(sb.parseFrom(os.toByteArray()), buf.toString());
+
+		os = new UnsafeByteArrayOutputStream();
+		Builder<HashMap> builder = Builder.register(HashMap.class);
+		Map services = new HashMap();
+		HashMap map = new HashMap();
+		services.put("test.service", "http://127.0.0.1:9010/test.service");
+		map.put("name", "qianlei");
+		map.put("password", "123455");
+		map.put("services", services);
+
+		builder.writeTo(map, os);
+		byte[] b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+		map = builder.parseFrom(b);
+		assertTrue(map.size() > 0);
+        assertEquals("http://127.0.0.1:9010/test.service", ((Map) map.get("services")).get("test.service"));
+
+		services = new ConcurrentHashMap();
+		services.put("test.service", "http://127.0.0.1:9010/test.service");
+		map.put("services", services);
+
+		os = new UnsafeByteArrayOutputStream();
+		builder.writeTo(map, os);
+		b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+		map = builder.parseFrom(b);
+		assertTrue(map.size() > 0);
+		assertEquals("http://127.0.0.1:9010/test.service", ((Map) map.get("services")).get("test.service"));
+
+		Node node1 = new Node();
+		Node node0 = new Node();
+		node0.value = "0";
+		node0.next = node1;
+		node1.value = "1";
+		node1.prev = node0;
+		// write.
+		Builder<Node> nodebuilder = Builder.register(Node.class);
+		os = new UnsafeByteArrayOutputStream();
+		nodebuilder.writeTo(node0, os);
+		b = os.toByteArray();
+		System.out.println("Node:"+b.length+":"+Bytes.bytes2hex(b));
+		// parse
+		node0 = nodebuilder.parseFrom(b);
+		assertEquals(node0, node0.prev);
+		assertEquals(node0, node0.next.prev);
+		assertEquals(node0.value, "0");
+	}
+    public static void main(String[] args) {

+        System.out.println(Modifier.isPublic(String.class.getModifiers()));

+    }
+    @Test
+	public void testWithFC() throws Exception
+	{
+		Builder<SimpleDO> builder = Builder.register(SimpleDO.class);
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+
+		SimpleDO sd = new SimpleDO();
+		sd.a = 1;
+		sd.b = 2;
+		sd.c = 3;
+		sd.str1 = "12345";
+		sd.str2 = "54321";
+		builder.writeTo(sd, os);
+		byte[] b = os.toByteArray();
+		System.out.println(b.length+":"+Bytes.bytes2hex(b));
+
+		sd = builder.parseFrom(b);
+		assertEquals(sd.a, 1);
+		assertEquals(sd.b, 2);
+		assertEquals(sd.c, 3);
+		assertEquals(sd.str1, "124");
+		System.out.println(sd.str2);
+	}
+
+	public enum Type
+	{
+		Lower, Normal, High;
+	}
+
+	static interface TestDO
+	{
+		String getName();
+		void setName(String name);
+		Type getType();
+		void setType(Type t);
+		int getArg();
+		void setArg(int arg);
+	}
+
+	static class TestDOImpl implements TestDO, Serializable
+	{
+	    private static final long serialVersionUID = 1L;
+		public String getName()
+		{
+			return "name";
+		}
+		public void setName(String name){}
+		public Type getType()
+		{
+			return Type.High;
+		}
+		public void setType(Type t){}
+		public int getArg()
+		{
+			return 28;
+		}
+		public void setArg(int arg){}
+	}
+
+	static class Bean implements Serializable
+	{
+	    private static final long serialVersionUID = 1L;
+		public int vi = 0;
+		public long vl = 100l;
+
+		boolean b = true;
+		boolean[] bs = {false, true};
+
+		String s1 = "1234567890";
+		String s2 = "1234567890一二三四五六七八九零";
+
+		private int i = 123123, ni = -12344, is[] = {1,2,3,4,-1,-2,-3,-4};
+        private short s = 12, ns = -76;
+		private double d = 12.345, nd = -12.345;
+		private long l = 1281447759383l, nl = -13445l;
+
+		private Boolean B = Boolean.FALSE;
+		private Integer I = -1234;
+		private Double D = new Double(1.23);
+		private String name = "qianlei";
+		private Type type = Type.Lower, type1 = Type.Normal;
+		private Type[] types = { Type.Lower, Type.Lower };
+
+		private Time time = null;
+
+		public Type getType()
+		{
+			return type;
+		}
+
+		public void setType(Type type)
+		{
+			this.type = type;
+		}
+
+		private ArrayList list = new ArrayList();
+		{
+			list.add(1);
+			list.add(2);
+			list.add(1308147);
+		}
+	}
+
+	static class MyList<T> extends ArrayList<T>
+	{
+		private int code = 12345;
+		private String id = "feedback";
+	}
+
+	static class MyMap<K, V> extends HashMap<K, V>
+	{
+		private int code = 12345;
+		private String id = "feedback";
+	}
+
+	static class Node implements Serializable
+	{
+        private static final long serialVersionUID = 1L;
+        Node prev = this;
+		Node next = this;
+		String value = "value";
+
+		@Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((value == null) ? 0 : value.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null) return false;
+            if (getClass() != obj.getClass()) return false;
+            Node other = (Node) obj;
+            if (value == null) {
+                if (other.value != null) return false;
+            } else if (!value.equals(other.value)) return false;
+            return true;
+        }
+	}
+
+	static class SerializableBean implements Serializable
+	{
+		private static final long serialVersionUID = -8949681707161463700L;
+
+		public int a = 0;
+		public long b = 100l;
+		boolean c = true;
+		String s1 = "1234567890";
+		String s2 = "1234567890一二三四五六七八九零";
+
+		public int hashCode()
+		{
+			return s1.hashCode() ^ s2.hashCode();
+		}
+
+		public boolean equals(Object obj)
+		{
+			if( obj == null ) return false;
+			if( obj == this ) return true;
+			if( obj instanceof SerializableBean )
+			{
+				SerializableBean sb = (SerializableBean)obj;
+				return this.a == sb.a && this.b == sb.b && this.c == sb.c && this.s1.equals(sb.s1) && this.s2.equals(sb.s2); 
+			}
+
+			return false;
+		}
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/DataInputOutputTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/DataInputOutputTest.java
new file mode 100644
index 0000000..cfaf8a0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/DataInputOutputTest.java
@@ -0,0 +1,125 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.dubbo;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.serialize.DataInput;
+import com.alibaba.dubbo.common.serialize.DataOutput;
+import com.alibaba.dubbo.common.serialize.support.dubbo.GenericDataInput;
+import com.alibaba.dubbo.common.serialize.support.dubbo.GenericDataOutput;
+
+public class DataInputOutputTest extends TestCase
+{
+	private static final String SMALL_STRING = DataInputOutputTest.class.getName(), BIG_STREAM = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+
+	private static final byte[] SMALL_BYTES = SMALL_STRING.getBytes(), BIG_BYTES = BIG_STREAM.getBytes();
+
+	public void testMain() throws Exception
+	{
+		// write.
+		UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+		DataOutput cos = new GenericDataOutput(os);
+		writeTest(cos);
+
+		// read.
+		byte[] b = os.toByteArray();
+		DataInput cis = new GenericDataInput(new UnsafeByteArrayInputStream(b));
+		readTest(cis);
+	}
+
+	private void writeTest(DataOutput out) throws IOException
+	{
+		out.writeShort((short)'a');
+		out.writeShort((short)-1);
+		out.writeShort((short)1234);
+		out.writeInt(0x22);
+		out.writeInt(-0x22);
+		out.writeInt(0x2222);
+		out.writeInt(-0x2222);
+		out.writeInt(0x222222);
+		out.writeInt(-0x222222);
+		out.writeInt(0x22222222);
+		out.writeInt(-0x22222222);
+		out.writeLong(0x22);
+		out.writeLong(-0x22);
+		out.writeLong(0x2222);
+		out.writeLong(-0x2222);
+		out.writeLong(0x222222);
+		out.writeLong(-0x222222);
+		out.writeLong(0x22222222);
+		out.writeLong(-0x22222222);
+		out.writeLong(0x2222222222l);
+		out.writeLong(-0x2222222222l);
+		out.writeLong(0x222222222222l);
+		out.writeLong(-0x222222222222l);
+		out.writeLong(0x22222222222222l);
+		out.writeLong(-0x22222222222222l);
+		out.writeLong(0x2222222222222222l);
+		out.writeLong(-0x2222222222222222l);
+		out.writeDouble(1212.454);
+		out.writeBytes(SMALL_BYTES);
+		out.writeUTF(SMALL_STRING);
+		out.writeBytes(BIG_BYTES);
+		out.flushBuffer();
+	}
+
+	private void readTest(DataInput in) throws IOException
+	{
+		assertEquals(in.readShort(), 'a');
+		assertEquals(in.readShort(), -1);
+		assertEquals(in.readShort(), 1234);
+		assertEquals(in.readInt(), 0x22);
+		assertEquals(in.readInt(), -0x22);
+		assertEquals(in.readInt(), 0x2222);
+		assertEquals(in.readInt(), -0x2222);
+		assertEquals(in.readInt(), 0x222222);
+		assertEquals(in.readInt(), -0x222222);
+		assertEquals(in.readInt(), 0x22222222);
+		assertEquals(in.readInt(), -0x22222222);
+		assertEquals(in.readLong(), 0x22);
+		assertEquals(in.readLong(), -0x22);
+		assertEquals(in.readLong(), 0x2222);
+		assertEquals(in.readLong(), -0x2222);
+		assertEquals(in.readLong(), 0x222222);
+		assertEquals(in.readLong(), -0x222222);
+		assertEquals(in.readLong(), 0x22222222);
+		assertEquals(in.readLong(), -0x22222222);
+		assertEquals(in.readLong(), 0x2222222222l);
+		assertEquals(in.readLong(), -0x2222222222l);
+		assertEquals(in.readLong(), 0x222222222222l);
+		assertEquals(in.readLong(), -0x222222222222l);
+		assertEquals(in.readLong(), 0x22222222222222l);
+		assertEquals(in.readLong(), -0x22222222222222l);
+		assertEquals(in.readLong(), 0x2222222222222222l);
+		assertEquals(in.readLong(), -0x2222222222222222l);
+		assertEquals(in.readDouble(), 1212.454);
+		assertSameArray(in.readBytes(), SMALL_BYTES);
+		assertEquals(in.readUTF(), SMALL_STRING);
+		assertSameArray(in.readBytes(), BIG_BYTES);
+	}
+
+	private static void assertSameArray(byte[] b1, byte[] b2)
+	{
+		assertEquals(b1.length, b2.length);
+		for(int i=0;i<b1.length;i++)
+			assertEquals(b1[i], b2[i]);
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.java
new file mode 100644
index 0000000..da67d72
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.java
@@ -0,0 +1,32 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.dubbo;
+
+import java.io.Serializable;
+
+public class SimpleDO implements Serializable
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public int a, b, c;
+
+	float d = 1.2f, e = 12.56f;
+
+	String str1 = "124", str2;
+
+	public int str3;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionFailTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionFailTest.java
new file mode 100644
index 0000000..f90368c
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionFailTest.java
@@ -0,0 +1,146 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.serialization;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.io.NotSerializableException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.model.Person;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * @author ding.lid
+ */
+public abstract class AbstractSerializationPersionFailTest extends AbstractSerializationTest {
+    @Test
+    public void test_Person() throws Exception {
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(new Person());
+            fail();
+        }
+        catch (NotSerializableException expected) {}
+        catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+        }
+    }
+
+    @Test
+    public void test_PersonList() throws Exception {
+        List<Person> args = new ArrayList<Person>();
+        args.add(new Person());
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        }
+        catch (NotSerializableException expected) {}
+        catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+        }
+    }
+
+    @Test
+    public void test_PersonSet() throws Exception {
+        Set<Person> args = new HashSet<Person>();
+        args.add(new Person());
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        }
+        catch (NotSerializableException expected) {}
+        catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+        }
+    }
+
+    @Test
+    public void test_IntPersonMap() throws Exception {
+        Map<Integer, Person> args = new HashMap<Integer, Person>();
+        args.put(1, new Person());
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        }
+        catch (NotSerializableException expected) {}
+        catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+        }
+    }
+
+    @Test
+    public void test_StringPersonMap() throws Exception {
+        Map<String, Person> args = new HashMap<String, Person>();
+        args.put("1", new Person());
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        }
+        catch (NotSerializableException expected) {}
+        catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+        }
+    }
+
+    @Test
+    public void test_StringPersonListMap() throws Exception {
+        Map<String, List<Person>> args = new HashMap<String, List<Person>>();
+
+        List<Person> sublist = new ArrayList<Person>();
+        sublist.add(new Person());
+        args.put("1", sublist);
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        }
+        catch (NotSerializableException expected) {}
+        catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+        }
+    }
+
+    @Test
+    public void test_PersonListList() throws Exception {
+        List<List<Person>> args = new ArrayList<List<Person>>();
+        List<Person> sublist = new ArrayList<Person>();
+        sublist.add(new Person());
+        args.add(sublist);
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        }
+        catch (NotSerializableException expected) {}
+        catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionOkTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionOkTest.java
new file mode 100644
index 0000000..4176a80
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionOkTest.java
@@ -0,0 +1,95 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.serialization;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.model.Person;
+
+/**
+ * @author ding.lid
+ */
+public abstract class AbstractSerializationPersionOkTest extends AbstractSerializationTest {
+    @Test
+    public void test_Person() throws Exception {
+        assertObject(new Person());
+    }
+
+    @Test
+    public void test_Person_withType() throws Exception {
+        assertObjectWithType(new Person(), Person.class);
+    }
+    
+    @Test
+    public void test_PersonList() throws Exception {
+        List<Person> args = new ArrayList<Person>();
+        args.add(new Person());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_PersonSet() throws Exception {
+        Set<Person> args = new HashSet<Person>();
+        args.add(new Person());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_IntPersonMap() throws Exception {
+        Map<Integer, Person> args = new HashMap<Integer, Person>();
+        args.put(1, new Person());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringPersonMap() throws Exception {
+        Map<String, Person> args = new HashMap<String, Person>();
+        args.put("1", new Person());
+
+        assertObject(args);
+    }
+    
+    @Test
+    public void test_StringPersonListMap() throws Exception {
+        Map<String, List<Person>> args = new HashMap<String, List<Person>>();
+
+        List<Person> sublist = new ArrayList<Person>();
+        sublist.add(new Person());
+        args.put("1", sublist);
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_PersonListList() throws Exception {
+        List<List<Person>> args = new ArrayList<List<Person>>();
+        List<Person> sublist = new ArrayList<Person>();
+        sublist.add(new Person());
+        args.add(sublist);
+
+        assertObject(args);
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationTest.java
new file mode 100644
index 0000000..46ebee3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationTest.java
@@ -0,0 +1,1213 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.serialization;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Time;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.model.AnimalEnum;
+import com.alibaba.dubbo.common.model.BizException;
+import com.alibaba.dubbo.common.model.BizExceptionNoDefaultConstructor;
+import com.alibaba.dubbo.common.model.SerializablePerson;
+import com.alibaba.dubbo.common.model.media.Image;
+import com.alibaba.dubbo.common.model.media.Image.Size;
+import com.alibaba.dubbo.common.model.media.Media;
+import com.alibaba.dubbo.common.model.media.Media.Player;
+import com.alibaba.dubbo.common.model.media.MediaContent;
+import com.alibaba.dubbo.common.model.person.BigPerson;
+import com.alibaba.dubbo.common.model.person.FullAddress;
+import com.alibaba.dubbo.common.model.person.PersonInfo;
+import com.alibaba.dubbo.common.model.person.PersonStatus;
+import com.alibaba.dubbo.common.model.person.Phone;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+public abstract class AbstractSerializationTest {
+    Serialization         serialization;
+
+    URL                   url                   = new URL("protocl", "1.1.1.1", 1234);
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+    static Random         random                = new Random();
+
+    // ================ Primitive Type ================ 
+
+    @Test
+    public void test_Bool() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBool(false);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertFalse(deserialize.readBool());
+
+        try {
+            deserialize.readBool();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Bool_Multi() throws Exception {
+        boolean[] array = new boolean[100];
+        for (int i = 0; i < array.length; i++) {
+            array[i] = random.nextBoolean();
+        }
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        for (boolean b : array) {
+            objectOutput.writeBool(b);
+        }
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        for (boolean b : array) {
+            assertEquals(b, deserialize.readBool());
+        }
+
+        try {
+            deserialize.readBool();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Byte() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeByte((byte) 123);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals((byte) 123, deserialize.readByte());
+
+        try {
+            deserialize.readByte();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Byte_Multi() throws Exception {
+        byte[] array = new byte[100];
+        random.nextBytes(array);
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        for (byte b : array) {
+            objectOutput.writeByte(b);
+        }
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        for (byte b : array) {
+            assertEquals(b, deserialize.readByte());
+        }
+
+        try {
+            deserialize.readByte();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Short() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeShort((short) 123);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals((short) 123, deserialize.readShort());
+
+        try {
+            deserialize.readShort();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Integer() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeInt(1);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        int i = deserialize.readInt();
+        assertEquals(1, i);
+
+        try {
+            deserialize.readInt();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Long() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeLong(123L);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(123L, deserialize.readLong());
+
+        try {
+            deserialize.readLong();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Float() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeFloat(1.28F);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertTrue(1.28F == deserialize.readFloat());
+
+        try {
+            deserialize.readFloat();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Double() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeDouble(1.28);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertTrue(1.28 == deserialize.readDouble());
+
+        try {
+            deserialize.readDouble();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_UtfString() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeUTF("123中华人民共和国");
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals("123中华人民共和国", deserialize.readUTF());
+
+        try {
+            deserialize.readUTF();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Bytes() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBytes("123中华人民共和国".getBytes());
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals("123中华人民共和国".getBytes(), deserialize.readBytes());
+
+        try {
+            deserialize.readBytes();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_BytesRange() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBytes("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, 9);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        byte[] expectedArray = new byte[9];
+        System.arraycopy("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, expectedArray, 0, expectedArray.length);
+        assertArrayEquals(expectedArray, deserialize.readBytes());
+
+        try {
+            deserialize.readBytes();
+            fail();
+        } catch (IOException expected) {}
+    }
+
+    // ================== Util methods ==================
+
+    <T> void assertObjectArray(T[] data, Class<T[]> clazz) throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, clazz.cast(deserialize.readObject()));
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    <T> void assertObjectArrayWithType(T[] data, Class<T[]> clazz) throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, clazz.cast(deserialize.readObject(clazz)));
+
+        try {
+            deserialize.readObject(clazz);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    <T> void assertObject(T data) throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(data, (T) deserialize.readObject());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    <T> void assertObjectWithType(T data, Class<T> clazz) throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(data, (T) deserialize.readObject(clazz));
+
+        try {
+            deserialize.readObject(clazz);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    // ================ Array Type ================ 
+    
+    @Test
+    public void test_boolArray() throws Exception {
+        boolean[] data = new boolean[] { true, false, true};
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject()));
+        
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_boolArray_withType() throws Exception {
+        boolean[] data = new boolean[] { true, false, true};
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject(boolean[].class)));
+        
+        try {
+            deserialize.readObject(boolean[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_charArray() throws Exception {
+        char[] data = new char[] { 'a', '中', '无' };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (char[]) deserialize.readObject());
+        
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_charArray_withType() throws Exception {
+        char[] data = new char[] { 'a', '中', '无' };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (char[]) deserialize.readObject(char[].class));
+        
+        try {
+            deserialize.readObject(char[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_shortArray() throws Exception {
+        short[] data = new short[] { 37, 39, 12 };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (short[]) deserialize.readObject());
+        
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_shortArray_withType() throws Exception {
+        short[] data = new short[] { 37, 39, 12 };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (short[]) deserialize.readObject(short[].class));
+        
+        try {
+            deserialize.readObject(short[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_intArray() throws Exception {
+        int[] data = new int[] { 234, 0, -1};
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (int[]) deserialize.readObject());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_intArray_withType() throws Exception {
+        int[] data = new int[] { 234, 0, -1};
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (int[]) deserialize.readObject(int[].class));
+
+        try {
+            deserialize.readObject(int[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_longArray() throws Exception {
+        long[] data = new long[] { 234, 0, -1};
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (long[]) deserialize.readObject());
+        
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_longArray_withType() throws Exception {
+        long[] data = new long[] { 234, 0, -1};
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (long[]) deserialize.readObject(long[].class));
+        
+        try {
+            deserialize.readObject(long[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_floatArray() throws Exception {
+        float[] data = new float[] { 37F, -3.14F, 123456.7F };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (float[]) deserialize.readObject(), 0.0001F);
+        
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_floatArray_withType() throws Exception {
+        float[] data = new float[] { 37F, -3.14F, 123456.7F };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (float[]) deserialize.readObject(float[].class), 0.0001F);
+        
+        try {
+            deserialize.readObject(float[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_doubleArray() throws Exception {
+        double[] data = new double[] { 37D, -3.14D, 123456.7D };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (double[]) deserialize.readObject(), 0.0001);
+        
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_doubleArray_withType() throws Exception {
+        double[] data = new double[] { 37D, -3.14D, 123456.7D };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (double[]) deserialize.readObject(double[].class), 0.0001);
+        
+        try {
+            deserialize.readObject(double[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_StringArray() throws Exception {
+        assertObjectArray(new String[] { "1", "b" }, String[].class);
+    }
+
+    @Test
+    public void test_StringArray_withType() throws Exception {
+        assertObjectArrayWithType(new String[] { "1", "b" }, String[].class);
+    }
+    
+    @Test
+    public void test_IntegerArray() throws Exception {
+        assertObjectArray(new Integer[] { 234, 0, -1}, Integer[].class);
+    }
+    
+    @Test
+    public void test_IntegerArray_withType() throws Exception {
+        assertObjectArrayWithType(new Integer[] { 234, 0, -1}, Integer[].class);
+    }
+    
+    @Test
+    public void test_EnumArray() throws Exception {
+        assertObjectArray(new AnimalEnum[] { AnimalEnum.bull, AnimalEnum.cat, AnimalEnum.dog, AnimalEnum.horse}, AnimalEnum[].class);
+    }
+    
+    @Test
+    public void test_EnumArray_withType() throws Exception {
+        assertObjectArrayWithType(new AnimalEnum[] { AnimalEnum.bull, AnimalEnum.cat, AnimalEnum.dog, AnimalEnum.horse}, AnimalEnum[].class);
+    }
+
+    // ================ Simple Type ================ 
+
+    @Test
+    public void test_SPerson() throws Exception {
+        assertObject(new SerializablePerson());
+    }
+
+    @Test
+    public void test_SPerson_withType() throws Exception {
+        assertObjectWithType(new SerializablePerson(), SerializablePerson.class);
+    }
+
+    @Test
+    public void test_BizException() throws Exception {
+        BizException e = new BizException("Hello");
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(e);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        Object read = deserialize.readObject();
+        assertEquals("Hello", ((BizException) read).getMessage());
+    }
+    
+    @Test
+    public void test_BizException_WithType() throws Exception {
+        BizException e = new BizException("Hello");
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(e);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        Object read = deserialize.readObject(BizException.class);
+        assertEquals("Hello", ((BizException) read).getMessage());
+    }
+    
+    @Test
+    public void test_BizExceptionNoDefaultConstructor() throws Exception {
+        BizExceptionNoDefaultConstructor e = new BizExceptionNoDefaultConstructor("Hello");
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(e);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        Object read = deserialize.readObject();
+        assertEquals("Hello", ((BizExceptionNoDefaultConstructor) read).getMessage());
+    }
+    
+    @Test
+    public void test_BizExceptionNoDefaultConstructor_WithType() throws Exception {
+        BizExceptionNoDefaultConstructor e = new BizExceptionNoDefaultConstructor("Hello");
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(e);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        Object read = deserialize.readObject(BizExceptionNoDefaultConstructor.class);
+        assertEquals("Hello", ((BizExceptionNoDefaultConstructor) read).getMessage());
+    }
+    
+    @Test
+    public void test_enum() throws Exception {
+        assertObject(AnimalEnum.dog);
+    }
+    
+    @Test
+    public void test_enum_withType() throws Exception {
+        assertObjectWithType(AnimalEnum.dog, AnimalEnum.class);
+    }
+    
+    @Test
+    public void test_Date() throws Exception {
+        assertObject(new Date());
+    }
+    
+    @Test
+    public void test_Date_withType() throws Exception {
+        assertObjectWithType(new Date(), Date.class);
+    }
+    
+    @Test
+    public void test_Time() throws Exception {
+        assertObject(new Time(System.currentTimeMillis()));
+    }
+    
+    @Test
+    public void test_Time_withType() throws Exception {
+        assertObjectWithType(new Time(System.currentTimeMillis()), Time.class);
+    }
+    
+    @Test
+    public void test_ByteWrap() throws Exception {
+        assertObject(new Byte((byte) 12));
+    }
+
+    @Test
+    public void test_ByteWrap_withType() throws Exception {
+        assertObjectWithType(new Byte((byte) 12), Byte.class);
+    }
+
+    @Test
+    public void test_LongWrap() throws Exception {
+        assertObject(new Long(12));
+    }
+    
+    @Test
+    public void test_LongWrap_withType() throws Exception {
+        assertObjectWithType(new Long(12), Long.class);
+    }
+    
+    @Test
+    public void test_BigInteger() throws Exception {
+        assertObject(new BigInteger("23423434234234234"));
+    }
+    
+    @Test
+    public void test_BigInteger_withType() throws Exception {
+        assertObjectWithType(new BigInteger("23423434234234234"), BigInteger.class);
+    }
+    
+    @Test
+    public void test_BigDecimal() throws Exception {
+        assertObject(new BigDecimal("23423434234234234.341274832341234235"));
+    }
+    
+    @Test
+    public void test_BigDecimal_withType() throws Exception {
+        assertObjectWithType(new BigDecimal("23423434234234234.341274832341234235"), BigDecimal.class);
+    }
+    
+    @Test
+    public void test_StringList_asListReturn() throws Exception {
+        List<String> args = Arrays.asList(new String[] { "1", "b" });
+        
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringArrayList() throws Exception {
+        List<String> args = new ArrayList<String>(Arrays.asList(new String[] { "1", "b" }));
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringSet() throws Exception {
+        Set<String> args = new HashSet<String>();
+        args.add("1");
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_LinkedHashMap() throws Exception {
+        LinkedHashMap<String, String> data = new LinkedHashMap<String, String>();
+        data.put("1", "a");
+        data.put("2", "b");
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        Object read = deserialize.readObject();
+        assertTrue(read instanceof LinkedHashMap);
+        @SuppressWarnings("unchecked")
+        String key1 = ((LinkedHashMap<String, String>)read).entrySet().iterator().next().getKey();
+        assertEquals("1", key1);
+        
+        assertEquals(data, read);
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_SPersonList() throws Exception {
+        List<SerializablePerson> args = new ArrayList<SerializablePerson>();
+        args.add(new SerializablePerson());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_SPersonSet() throws Exception {
+        Set<SerializablePerson> args = new HashSet<SerializablePerson>();
+        args.add(new SerializablePerson());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_IntSPersonMap() throws Exception {
+        Map<Integer, SerializablePerson> args = new HashMap<Integer, SerializablePerson>();
+        args.put(1, new SerializablePerson());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringSPersonMap() throws Exception {
+        Map<String, SerializablePerson> args = new HashMap<String, SerializablePerson>();
+        args.put("1", new SerializablePerson());
+
+        assertObject(args);
+    }
+
+    // ================ Complex Collection Type ================ 
+
+    @Test
+    public void test_StringSPersonListMap() throws Exception {
+        Map<String, List<SerializablePerson>> args = new HashMap<String, List<SerializablePerson>>();
+
+        List<SerializablePerson> sublist = new ArrayList<SerializablePerson>();
+        sublist.add(new SerializablePerson());
+        args.put("1", sublist);
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_SPersonListList() throws Exception {
+        List<List<SerializablePerson>> args = new ArrayList<List<SerializablePerson>>();
+        List<SerializablePerson> sublist = new ArrayList<SerializablePerson>();
+        sublist.add(new SerializablePerson());
+        args.add(sublist);
+
+        assertObject(args);
+    }
+    
+    // ================ complex POJO =============
+    
+    BigPerson bigPerson;
+    {
+        bigPerson = new BigPerson();
+        bigPerson.setPersonId("superman111");
+        bigPerson.setLoginName("superman");
+        bigPerson.setStatus(PersonStatus.ENABLED);
+        bigPerson.setEmail("sm@1.com");
+        bigPerson.setPenName("pname");
+
+        ArrayList<Phone> phones = new ArrayList<Phone>();
+        Phone phone1 = new Phone("86", "0571", "87654321", "001");
+        Phone phone2 = new Phone("86", "0571", "87654322", "002");
+        phones.add(phone1);
+        phones.add(phone2);
+        
+        PersonInfo pi = new PersonInfo();
+        pi.setPhones(phones);
+        Phone fax = new Phone("86", "0571", "87654321", null);
+        pi.setFax(fax);
+        FullAddress addr = new FullAddress("CN", "zj", "3480", "wensanlu", "315000");
+        pi.setFullAddress(addr);
+        pi.setMobileNo("13584652131");
+        pi.setMale(true);
+        pi.setDepartment("b2b");
+        pi.setHomepageUrl("www.capcom.com");
+        pi.setJobTitle("qa");
+        pi.setName("superman");
+        
+        bigPerson.setInfoProfile(pi);
+    }
+    
+    @Test
+    public void test_BigPerson() throws Exception {
+        assertObject(bigPerson);
+    }
+    
+    @Test
+    public void test_BigPerson_WithType() throws Exception {
+        assertObjectWithType(bigPerson, BigPerson.class);
+    }
+    
+    MediaContent mediaContent;
+    {
+        Media media = new Media();
+        media.setUri("uri://中华人民共和国");
+        media.setTitle("title");
+        media.setWidth(1239);
+        media.setHeight(1938);
+        media.setFormat("format-xxxx");
+        media.setDuration(93419235);
+        media.setSize(3477897);
+        media.setBitrate(94523);
+        List<String> persons = new ArrayList<String>();
+        persons.add("jerry");
+        persons.add("tom");
+        persons.add("lucy");
+        media.setPersons(persons);
+        media.setCopyright("1999-2011");
+        media.setPlayer(Player.FLASH);
+        
+        List<Image> images = new ArrayList<Image>();
+        for(int i = 0; i < 10; ++i) {
+            Image image = new Image();
+            image.setUri("url" + i);
+            if(i % 2 == 0) image.setTitle("title" + i);
+            image.setWidth(34 + i);
+            image.setHeight(2323 + i);
+            image.setSize((i % 2 == 0) ? Size.SMALL : Size.LARGE);
+            
+            images.add(image);
+        }
+        
+       mediaContent = new MediaContent(media, images);
+    }
+    
+    @Test
+    public void test_MediaContent() throws Exception {
+        assertObject(mediaContent);
+    }
+    
+    @Test
+    public void test_MediaContent_WithType() throws Exception {
+        assertObjectWithType(mediaContent, MediaContent.class);
+    }
+    
+    @Test
+    public void test_MultiObject() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBool(false);
+        objectOutput.writeObject(bigPerson);
+        objectOutput.writeByte((byte) 23);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.writeInt(-23);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(false, deserialize.readBool());
+        assertEquals(bigPerson, deserialize.readObject());
+        assertEquals((byte) 23, deserialize.readByte());
+        assertEquals(mediaContent, deserialize.readObject());
+        assertEquals(-23, deserialize.readInt());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    @Test
+    public void test_MultiObject_WithType() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBool(false);
+        objectOutput.writeObject(bigPerson);
+        objectOutput.writeByte((byte) 23);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.writeInt(-23);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(false, deserialize.readBool());
+        assertEquals(bigPerson, deserialize.readObject(BigPerson.class));
+        assertEquals((byte) 23, deserialize.readByte());
+        assertEquals(mediaContent, deserialize.readObject(MediaContent.class));
+        assertEquals(-23, deserialize.readInt());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+    
+    
+    // abnormal case 
+    
+    @Test
+    public void test_MediaContent_badStream() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.flushBuffer();
+
+        byte[] byteArray = byteArrayOutputStream.toByteArray();
+        for (int i = 0; i < byteArray.length; i++) {
+            if(i%3 == 0) {
+                byteArray[i] = (byte)~byteArray[i];
+            }
+        }
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+        
+        try {
+            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+            @SuppressWarnings("unused") // local variable, convenient for debug
+            Object read = deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+            System.out.println(expected);
+        }
+    }
+    
+    @Test
+    public void test_MediaContent_WithType_badStream() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.flushBuffer();
+
+        byte[] byteArray = byteArrayOutputStream.toByteArray();
+        for (int i = 0; i < byteArray.length; i++) {
+            if(i%3 == 0) {
+                byteArray[i] = (byte)~byteArray[i];
+            }
+        }
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+        
+        try {
+            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+            @SuppressWarnings("unused") // local variable, convenient for debug
+            Object read = deserialize.readObject(MediaContent.class);
+            fail();
+        } catch (IOException expected) {
+            System.out.println(expected);
+        }
+    }
+    
+    
+    @Test(timeout=3000)
+    public void test_LoopReference() throws Exception {
+        Map<String, Object> map= new HashMap<String, Object>();
+        map.put("k1", "v1");
+        map.put("self", map);
+        
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(map);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> output = (Map<String, Object>) deserialize.readObject();
+        
+        assertEquals("v1", output.get("k1"));
+        assertSame(output, output.get("self"));
+    }

+    

+    // ================ final field test ================

+    

+    @Test

+    public void test_URL_mutable_withType() throws Exception {

+        URL data = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");

+

+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);

+        objectOutput.writeObject(data);

+        objectOutput.flushBuffer();

+

+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(

+                byteArrayOutputStream.toByteArray());

+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);

+

+        URL actual = (URL) deserialize.readObject(URL.class);

+        assertEquals(data, actual);

+        assertEquals(data.getParameters(), actual.getParameters());

+

+        try {

+            deserialize.readObject();

+            fail();

+        } catch (IOException expected) {

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/CompactedJavaSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/CompactedJavaSerializationTest.java
new file mode 100644
index 0000000..951ebce
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/CompactedJavaSerializationTest.java
@@ -0,0 +1,28 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.serialization;
+
+import com.alibaba.dubbo.common.serialize.support.java.CompactedJavaSerialization;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class CompactedJavaSerializationTest extends AbstractSerializationPersionFailTest {
+    {
+        serialization = new CompactedJavaSerialization();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/DubboSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/DubboSerializationTest.java
new file mode 100644
index 0000000..f497a16
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/DubboSerializationTest.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.serialization;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.serialize.support.dubbo.DubboSerialization;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class DubboSerializationTest extends AbstractSerializationPersionFailTest {
+    {
+        serialization = new DubboSerialization();
+    }
+    
+    /**
+     * @desc:DubboSerialization error: java.lang.IllegalAccessError: tried to access class java.util.Arrays$ArrayList from class com.alibaba.dubbo.common.serialize.support.dubbo.Builder$bc6
+     * @reason:in writeObject method, the first line is :java.util.Arrays$ArrayList v = (java.util.Arrays$ArrayList)$1; java.util.Arrays$ArrayList is a inter static class ,can not access.
+     * @tradeoff: how to resolve:we need change writeObject method, replace the first line with java.util.ArrayList v = new java.util.ArrayList((List)$1) , and in the same time, modify the defaultArg method ,return special construct args for ArrayList ... too ugly to support.  
+     */
+    @Ignore
+    @Test
+    public void test_StringList_asListReturn() throws Exception {
+        super.test_StringList_asListReturn();
+    }
+
+    // FIXME
+    @Ignore("StackOverflowError")
+    @Test(timeout=3000)
+    public void test_LoopReference() throws Exception {}

+    

+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FastJsonSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FastJsonSerializationTest.java
new file mode 100644
index 0000000..cd30285
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FastJsonSerializationTest.java
@@ -0,0 +1,266 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.serialization;
+
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.model.media.MediaContent;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.support.json.FastJsonSerialization;
+import com.alibaba.fastjson.JSONException;
+
+/**
+ * FIXME FastJson Serialization的失败,被暂时被忽略
+ * 
+ * @author ding.lid
+ */
+public class FastJsonSerializationTest extends AbstractSerializationPersionOkTest {
+    {
+        serialization = new FastJsonSerialization();
+    }
+    
+    @Ignore // FIXME
+    @Test
+    public void test_BytesRange() throws Exception {}
+    
+    @Ignore("bool[] type missing to JSONArray")
+    @Test
+    public void test_boolArray() throws Exception {}
+    
+    @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: create asm serilizer error, class char")
+    @Test
+    public void test_charArray() throws Exception {}
+    
+    @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: create asm serilizer error, class char")
+    @Test
+    public void test_charArray_withType() throws Exception {}
+    
+    @Ignore("short[] type missing to JSONArray")
+    @Test
+    public void test_shortArray() throws Exception {}
+    
+    @Ignore("int[] type missing to JSONArray")
+    @Test
+    public void test_intArray() throws Exception {}
+    
+    @Ignore("long[] type missing to JSONArray")
+    @Test
+    public void test_longArray() throws Exception {}
+    
+    @Ignore("float[] type missing to JSONArray")
+    @Test
+    public void test_floatArray() throws Exception {}
+    
+    @Ignore("double[] type missing to JSONArray")
+    @Test
+    public void test_doubleArray() throws Exception {}
+
+    @Ignore("String[] type missing to JSONArray")
+    @Test
+    public void test_StringArray() throws Exception {}
+    
+    @Ignore("Integer[] type missing to JSONArray")
+    @Test
+    public void test_IntegerArray() throws Exception {}    
+    
+    @Ignore("Integer[] type missing to JSONArray")
+    @Test
+    public void test_EnumArray() throws Exception {}
+    
+    @Ignore("type mising to Long")
+    @Test
+    public void test_Date() throws Exception {}
+    
+    @Ignore("type mising to Long")
+    @Test
+    public void test_Time() throws Exception {}
+    
+    @Ignore("com.alibaba.fastjson.JSONException: create asm deserializer error, java.sql.Time")
+    @Test
+    public void test_Time_withType() throws Exception {}
+    
+    @Ignore("type mising to Integer")
+    @Test
+    public void test_ByteWrap() throws Exception {}
+    
+    @Ignore("type mising to Integer")
+    @Test
+    public void test_LongWrap() throws Exception {}
+    
+    @Ignore("type mising to Long")
+    @Test
+    public void test_BigInteger() throws Exception {}
+
+    @Ignore("SPerson type missing")
+    @Test
+    public void test_SPerson() throws Exception {}   
+    
+    @Ignore("BizException type missing to Map")
+    @Test
+    public void test_BizException() throws Exception {}
+    
+    @Ignore("BizExceptionNoDefaultConstructor type missing to Map")
+    @Test
+    public void test_BizExceptionNoDefaultConstructor() throws Exception {}
+    
+    // FIXME 没有缺省构造函数失败
+    @Ignore("NoDefaultConstructor")
+    @Test
+    public void test_BizExceptionNoDefaultConstructor_WithType() throws Exception {}
+  
+    @Ignore("Enum type missing to String")
+    @Test
+    public void test_enum() throws Exception {}
+      
+    @Ignore("String set missing to JSONArray")
+    @Test
+    public void test_StringSet() throws Exception {}
+    
+    @Ignore("LinkedHashMap type missing to Map")
+    @Test
+    public void test_LinkedHashMap() throws Exception {}
+      
+      
+    @Ignore("person type missing")
+    @Test
+    public void test_SPersonList() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_SPersonSet() throws Exception {}
+    
+    @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: illegal identifier : 1")
+    @Test
+    public void test_IntSPersonMap() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_StringSPersonMap() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_StringSPersonListMap() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_SPersonListList() throws Exception {}
+    
+    @Ignore("BigPerson type missing")
+    @Test
+    public void test_BigPerson() throws Exception {}
+    
+    @Ignore("MediaContent type missing")
+    @Test
+    public void test_MediaContent() throws Exception {}
+    
+    @Ignore("type missing")
+    @Test
+    public void test_MultiObject() throws Exception {}
+    
+    @Test
+    public void test_MediaContent_badStream() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.flushBuffer();
+
+        byte[] byteArray = byteArrayOutputStream.toByteArray();
+        for (int i = 0; i < byteArray.length; i++) {
+            if(i%3 == 0) {
+                byteArray[i] = (byte)~byteArray[i];
+            }
+        }
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+        
+        try {
+            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+            @SuppressWarnings("unused") // local variable, convenient for debug
+            Object read = deserialize.readObject();
+            fail();
+        } catch (JSONException expected) {
+            System.out.println(expected);
+        }
+    }
+    
+    @Test
+    public void test_MediaContent_WithType_badStream() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.flushBuffer();
+
+        byte[] byteArray = byteArrayOutputStream.toByteArray();
+        for (int i = 0; i < byteArray.length; i++) {
+            if(i%3 == 0) {
+                byteArray[i] = (byte)~byteArray[i];
+            }
+        }
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+        
+        try {
+            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+            @SuppressWarnings("unused") // local variable, convenient for debug
+            Object read = deserialize.readObject(MediaContent.class);
+            fail();
+        } catch (JSONException expected) {
+            System.out.println(expected);
+        }
+    }

+    

+    // FIXME DUBBO-63

+    @Ignore

+    @Test

+    public void test_URL_mutable_withType() throws Exception {}
+    
+    @Ignore
+    @Test(timeout=3000)
+    public void test_LoopReference() throws Exception {}
+    
+    // ========== Person
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_Person() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_PersonList() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_PersonSet() throws Exception {}
+    
+    @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: illegal identifier : 1")
+    @Test
+    public void test_IntPersonMap() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_StringPersonMap() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_StringPersonListMap() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_PersonListList() throws Exception {}
+  
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/Hessian2SerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/Hessian2SerializationTest.java
new file mode 100644
index 0000000..2e20c49
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/Hessian2SerializationTest.java
@@ -0,0 +1,229 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.serialization;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Hessian2SerializationTest extends AbstractSerializationPersionFailTest {
+    {
+        serialization = new Hessian2Serialization();
+    }
+    
+    // Hessian2 
+    
+    @Test
+    public void test_boolArray_withType() throws Exception {
+        boolean[] data = new boolean[] { true, false, true};
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject(boolean[].class)));
+        
+        try {
+            deserialize.readObject(boolean[].class);
+            fail();
+        } 
+        catch (ArrayIndexOutOfBoundsException e) {} 
+        // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+        // 容忍这个问题!!
+    }
+    
+    @Ignore("type missing, char[] -> String")
+    @Test
+    public void test_charArray() throws Exception {}
+    
+    @Test
+    public void test_shortArray_withType() throws Exception {
+        short[] data = new short[] { 37, 39, 12 };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (short[]) deserialize.readObject(short[].class));
+        
+        try {
+            deserialize.readObject(short[].class);
+            fail();
+        } 
+        catch (ArrayIndexOutOfBoundsException e) {} 
+        // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+        // 容忍这个问题!!
+    }
+    
+    @Test
+    public void test_intArray_withType() throws Exception {
+        int[] data = new int[] { 234, 0, -1};
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (int[]) deserialize.readObject());
+
+        try {
+            deserialize.readObject(int[].class);
+            fail();
+        } 
+        catch (ArrayIndexOutOfBoundsException e) {} 
+        // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+        // 容忍这个问题!!
+    }
+    
+    @Test
+    public void test_longArray_withType() throws Exception {
+        long[] data = new long[] { 234, 0, -1};
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (long[]) deserialize.readObject());
+
+        try {
+            deserialize.readObject(long[].class);
+            fail();
+        } 
+        catch (ArrayIndexOutOfBoundsException e) {} 
+        // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+        // 容忍这个问题!!
+    }
+    
+    @Test
+    public void test_floatArray_withType() throws Exception {
+        float[] data = new float[] { 37F, -3.14F, 123456.7F };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (float[]) deserialize.readObject(), 0.0001F);
+        
+        try {
+            deserialize.readObject(float[].class);
+            fail();
+        } 
+        catch (ArrayIndexOutOfBoundsException e) {} 
+        // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+        // 容忍这个问题!!
+    }
+    
+    @Test
+    public void test_doubleArray_withType() throws Exception {
+        double[] data = new double[] { 37D, -3.14D, 123456.7D };
+        
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+        
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        
+        assertArrayEquals(data, (double[]) deserialize.readObject(double[].class), 0.0001);
+        
+        try {
+            deserialize.readObject(double[].class);
+            fail();
+        } 
+        catch (ArrayIndexOutOfBoundsException e) {} 
+        // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+        // 容忍这个问题!!
+    }
+    
+    @Test
+    public void test_StringArray_withType() throws Exception {
+        
+        String[] data = new String[] { "1", "b" };
+        
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, deserialize.readObject(String[].class));
+
+        try {
+            deserialize.readObject(String[].class);
+            fail();
+        } 
+        catch (ArrayIndexOutOfBoundsException e) {} 
+        // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+        // 容忍这个问题!!
+    }
+    
+    @Ignore("type missing, Byte -> Integer")
+    @Test
+    public void test_ByteWrap() throws Exception { }
+    
+    
+    // FIXME
+    @Ignore("com.alibaba.com.caucho.hessian.io.HessianProtocolException: 'java.math.BigInteger' could not be instantiated")
+    @Test
+    public void test_BigInteger() throws Exception {}
+    
+    // FIXME
+    @Ignore("com.alibaba.com.caucho.hessian.io.HessianProtocolException: 'java.math.BigInteger' could not be instantiated")
+    @Test
+    public void test_BigInteger_withType() throws Exception {}
+    
+    // FIXME
+    @Ignore("Bad Stream read other type data")
+    @Test
+    public void test_MediaContent_badStream() throws Exception {}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JavaSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JavaSerializationTest.java
new file mode 100644
index 0000000..46f7c57
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JavaSerializationTest.java
@@ -0,0 +1,28 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.serialization;
+
+import com.alibaba.dubbo.common.serialize.support.java.JavaSerialization;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class JavaSerializationTest extends AbstractSerializationPersionFailTest {
+    {
+        serialization = new JavaSerialization();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JsonSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JsonSerializationTest.java
new file mode 100644
index 0000000..3fff4e0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JsonSerializationTest.java
@@ -0,0 +1,227 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.serialize.serialization;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.serialize.support.json.JsonSerialization;
+
+/**
+ * FIXME Json Serialization的失败,被暂时被忽略
+ * 
+ * @author ding.lid
+ *
+ */
+public class JsonSerializationTest extends AbstractSerializationPersionOkTest {
+    {
+        serialization = new JsonSerialization();
+    }
+    
+    // FIXME
+    @Ignore
+    @Test
+    public void test_BytesRange() throws Exception {}
+    
+    @Ignore("bool[] type missing to List<Boolean>")
+    @Test
+    public void test_boolArray() throws Exception {}
+    
+    @Ignore("char[] type missing to List<Charator>")
+    @Test
+    public void test_charArray() throws Exception {}
+    
+    @Ignore("short[] type missing to List<Short>")
+    @Test
+    public void test_shortArray() throws Exception {}
+    
+    @Ignore("int[] type missing to List<Integer>")
+    @Test
+    public void test_intArray() throws Exception {}
+    
+    @Ignore("long[] type missing to List<Long>")
+    @Test
+    public void test_longArray() throws Exception {}
+    
+    @Ignore
+    @Test
+    public void test_floatArray() throws Exception {}
+    
+    @Ignore
+    @Test
+    public void test_doubleArray() throws Exception {}
+    
+    @Ignore("String[] type missing to List<String>")
+    @Test
+    public void test_StringArray() throws Exception {}
+    
+    @Ignore("Integer[] type missing to List<Integer>")
+    @Test
+    public void test_IntegerArray() throws Exception {}
+    
+    @Ignore
+    @Test
+    public void test_EnumArray() throws Exception {}
+    
+    @Ignore
+    @Test
+    public void test_Date() throws Exception {}
+    
+    @Ignore("毫秒部分丢失成0")
+    @Test
+    public void test_Date_withType() throws Exception {}
+    
+    @Ignore
+    @Test
+    public void test_Time() throws Exception {}
+
+    @Ignore
+    @Test
+    public void test_Time_withType() throws Exception {}
+    
+    @Ignore("Byte type missing to Long")
+    @Test
+    public void test_ByteWrap() throws Exception {}
+    
+    @Ignore("BigInteger type missing to Long")
+    @Test
+    public void test_BigInteger() throws Exception {}
+
+    @Ignore
+    @Test
+    public void test_BigDecimal() throws Exception {}
+    
+    @Ignore
+    @Test
+    public void test_BigDecimal_withType() throws Exception {}
+    
+    @Ignore("BizException type missing")
+    @Test
+    public void test_BizException() throws Exception {}
+    
+    @Ignore("BizExceptionNoDefaultConstructor type missing")
+    @Test
+    public void test_BizExceptionNoDefaultConstructor() throws Exception {}
+    
+    @Ignore("NoDefaultConstructor")
+    @Test
+    public void test_BizExceptionNoDefaultConstructor_WithType() throws Exception {}
+    
+    @Ignore("Enum type missing")
+    @Test
+    public void test_enum() throws Exception {}
+    
+    @Ignore("Set type missing to Map")
+    @Test
+    public void test_StringSet() throws Exception {}
+    
+    @Ignore("LinkedHashMap type missing to Map")
+    @Test
+    public void test_LinkedHashMap() throws Exception {}
+    
+    // 
+    
+    @Ignore("SPerson type missing")
+    @Test
+    public void test_SPerson() throws Exception {}
+    
+    @Ignore("SPerson type missing")
+    @Test
+    public void test_SPersonList() throws Exception {}
+    
+    @Ignore("SPerson type missing")
+    @Test
+    public void test_SPersonSet() throws Exception {}
+    
+    @Ignore("SPerson type missing")
+    @Test
+    public void test_IntSPersonMap() throws Exception {}
+    
+    @Ignore("SPerson type missing")
+    @Test
+    public void test_StringSPersonMap() throws Exception {}
+    
+    @Ignore("SPerson type missing")
+    @Test
+    public void test_StringSPersonListMap() throws Exception {}
+    
+    @Ignore("SPerson type missing")
+    @Test
+    public void test_SPersonListList() throws Exception {}
+    
+    @Ignore("type missing")
+    @Test
+    public void test_BigPerson() throws Exception {}
+    
+    @Ignore("type missing")
+    @Test
+    public void test_BigPerson_WithType() throws Exception {}
+    
+    @Ignore("type missing")
+    @Test
+    public void test_MediaContent() throws Exception {}
+    
+    @Ignore("type missing")
+    @Test
+    public void test_MediaContent_WithType() throws Exception {}
+    
+    @Ignore("type missing")
+    @Test
+    public void test_MultiObject() throws Exception {}
+    
+    @Ignore("type missing")
+    @Test
+    public void test_MultiObject_WithType() throws Exception {}
+    
+    @Ignore
+    @Test
+    public void test_LoopReference() throws Exception {}
+    

+    // FIXME DUBBO-63

+    @Ignore

+    @Test

+    public void test_URL_mutable_withType() throws Exception {}

+    
+    // Person
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_Person() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_PersonList() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_PersonSet() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_IntPersonMap() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_StringPersonMap() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_StringPersonListMap() throws Exception {}
+    
+    @Ignore("person type missing")
+    @Test
+    public void test_PersonListList() throws Exception {}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/AtomicPositiveIntegerTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/AtomicPositiveIntegerTest.java
new file mode 100644
index 0000000..a776182
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/AtomicPositiveIntegerTest.java
@@ -0,0 +1,161 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+
+/**
+ * @author ding.lid
+ */
+public class AtomicPositiveIntegerTest {
+    AtomicPositiveInteger i1 = new AtomicPositiveInteger();
+
+    AtomicPositiveInteger i2 = new AtomicPositiveInteger(127);
+
+    AtomicPositiveInteger i3 = new AtomicPositiveInteger(Integer.MAX_VALUE);
+
+    @Test
+    public void test_get() throws Exception {
+        assertEquals(0, i1.get());
+        assertEquals(127, i2.get());
+        assertEquals(Integer.MAX_VALUE, i3.get());
+    }
+
+    @Test
+    public void test_set() throws Exception {
+        i1.set(100);
+        assertEquals(100, i1.get());
+
+        try {
+            i1.set(-1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            assertThat(expected.getMessage(),
+                    allOf(containsString("new value"), containsString("< 0")));
+        }
+    }
+
+    @Test
+    public void test_getAndIncrement() throws Exception {
+        int get = i1.getAndIncrement();
+        assertEquals(0, get);
+        assertEquals(1, i1.get());
+
+        get = i2.getAndIncrement();
+        assertEquals(127, get);
+        assertEquals(128, i2.get());
+
+        get = i3.getAndIncrement();
+        assertEquals(Integer.MAX_VALUE, get);
+        assertEquals(0, i3.get());
+    }
+
+    @Test
+    public void test_getAndDecrement() throws Exception {
+        int get = i1.getAndDecrement();
+        assertEquals(0, get);
+        assertEquals(Integer.MAX_VALUE, i1.get());
+
+        get = i2.getAndDecrement();
+        assertEquals(127, get);
+        assertEquals(126, i2.get());
+
+        get = i3.getAndDecrement();
+        assertEquals(Integer.MAX_VALUE, get);
+        assertEquals(Integer.MAX_VALUE - 1, i3.get());
+    }
+
+    @Test
+    public void test_incrementAndGet() throws Exception {
+        int get = i1.incrementAndGet();
+        assertEquals(1, get);
+        assertEquals(1, i1.get());
+
+        get = i2.incrementAndGet();
+        assertEquals(128, get);
+        assertEquals(128, i2.get());
+
+        get = i3.incrementAndGet();
+        assertEquals(0, get);
+        assertEquals(0, i3.get());
+    }
+
+    @Test
+    public void test_decrementAndGet() throws Exception {
+        int get = i1.decrementAndGet();
+        assertEquals(Integer.MAX_VALUE, get);
+        assertEquals(Integer.MAX_VALUE, i1.get());
+
+        get = i2.decrementAndGet();
+        assertEquals(126, get);
+        assertEquals(126, i2.get());
+
+        get = i3.decrementAndGet();
+        assertEquals(Integer.MAX_VALUE - 1, get);
+        assertEquals(Integer.MAX_VALUE - 1, i3.get());
+    }
+
+    @Test
+    public void test_getAndSet() throws Exception {
+        int get = i1.getAndSet(100);
+        assertEquals(0, get);
+        assertEquals(100, i1.get());
+
+        try {
+            i1.getAndSet(-1);
+        } catch (IllegalArgumentException expected) {
+            assertThat(expected.getMessage(),
+                    allOf(containsString("new value"), containsString("< 0")));
+        }
+    }
+
+    @Test
+    public void test_getAndAnd() throws Exception {
+        int get = i1.getAndAdd(3);
+        assertEquals(0, get);
+        assertEquals(3, i1.get());
+
+        get = i2.getAndAdd(3);
+        assertEquals(127, get);
+        assertEquals(127 + 3, i2.get());
+
+        get = i3.getAndAdd(3);
+        assertEquals(Integer.MAX_VALUE, get);
+        assertEquals(2, i3.get());
+    }
+
+
+    @Test
+    public void test_addAndGet() throws Exception {
+        int get = i1.addAndGet(3);
+        assertEquals(3, get);
+        assertEquals(3, i1.get());
+
+        get = i2.addAndGet(3);
+        assertEquals(127 + 3, get);
+        assertEquals(127 + 3, i2.get());
+
+        get = i3.addAndGet(3);
+        assertEquals(2, get);
+        assertEquals(2, i3.get());
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CollectionUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CollectionUtilsTest.java
new file mode 100644
index 0000000..e8e6f24
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CollectionUtilsTest.java
@@ -0,0 +1,150 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+/**
+ * @author ding.lid
+ */
+public class CollectionUtilsTest {
+    @Test
+    public void test_sort() throws Exception {
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(100);
+        list.add(10);
+        list.add(20);
+
+        List<Integer> expected = new ArrayList<Integer>();
+        expected.add(10);
+        expected.add(20);
+        expected.add(100);
+
+        assertEquals(expected, CollectionUtils.sort(list));
+    }
+
+    @Test
+    public void test_sort_null() throws Exception {
+        assertNull(CollectionUtils.sort(null));
+
+        assertTrue(CollectionUtils.sort(new ArrayList<Integer>()).isEmpty());
+    }
+
+    @Test
+    public void test_sortSimpleName() throws Exception {
+        List<String> list = new ArrayList<String>();
+        list.add("aaa.z");
+        list.add("b");
+        list.add(null);
+        list.add("zzz.a");
+        list.add("c");
+        list.add(null);
+
+        List<String> sorted = CollectionUtils.sortSimpleName(list);
+        assertNull(sorted.get(0));
+        assertNull(sorted.get(1));
+    }
+
+    @Test
+    public void test_sortSimpleName_null() throws Exception {
+        assertNull(CollectionUtils.sortSimpleName(null));
+
+        assertTrue(CollectionUtils.sortSimpleName(new ArrayList<String>()).isEmpty());
+    }
+
+    @Test
+    public void test_splitAll() throws Exception {
+        assertNull(CollectionUtils.splitAll(null, null));
+        assertNull(CollectionUtils.splitAll(null, "-"));
+
+        assertTrue(CollectionUtils.splitAll(new HashMap<String, List<String>>(), "-").isEmpty());
+
+        Map<String, List<String>> input = new HashMap<String, List<String>>();
+        input.put("key1", Arrays.asList("1:a", "2:b", "3:c"));
+        input.put("key2", Arrays.asList("1:a", "2:b"));
+        input.put("key3", null);
+        input.put("key4", new ArrayList<String>());
+
+        Map<String, Map<String, String>> expected = new HashMap<String, Map<String, String>>();
+        expected.put("key1", CollectionUtils.toStringMap("1", "a", "2", "b", "3", "c"));
+        expected.put("key2", CollectionUtils.toStringMap("1", "a", "2", "b"));
+        expected.put("key3", null);
+        expected.put("key4", new HashMap<String, String>());
+
+        assertEquals(expected, CollectionUtils.splitAll(input, ":"));
+    }
+
+    @Test
+    public void test_joinAll() throws Exception {
+        assertNull(CollectionUtils.joinAll(null, null));
+        assertNull(CollectionUtils.joinAll(null, "-"));
+
+        Map<String, List<String>> expected = new HashMap<String, List<String>>();
+        expected.put("key1", Arrays.asList("1:a", "2:b", "3:c"));
+        expected.put("key2", Arrays.asList("1:a", "2:b"));
+        expected.put("key3", null);
+        expected.put("key4", new ArrayList<String>());
+
+        Map<String, Map<String, String>> input = new HashMap<String, Map<String, String>>();
+        input.put("key1", CollectionUtils.toStringMap("1", "a", "2", "b", "3", "c"));
+        input.put("key2", CollectionUtils.toStringMap("1", "a", "2", "b"));
+        input.put("key3", null);
+        input.put("key4", new HashMap<String, String>());
+
+        Map<String, List<String>> output = CollectionUtils.joinAll(input, ":");
+        for (Map.Entry<String, List<String>> entry : output.entrySet()) {
+            if (entry.getValue() == null)
+                continue;
+            Collections.sort(entry.getValue());
+        }
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void test_mapEquals() throws Exception {
+        assertTrue(CollectionUtils.mapEquals(null, null));
+        assertFalse(CollectionUtils.mapEquals(null, new HashMap<String, String>()));
+        assertFalse(CollectionUtils.mapEquals(new HashMap<String, String>(), null));
+        
+        assertTrue(CollectionUtils.mapEquals(CollectionUtils.toStringMap("1", "a", "2", "b"), CollectionUtils.toStringMap("1", "a", "2", "b")));
+        assertFalse(CollectionUtils.mapEquals(CollectionUtils.toStringMap("1", "a"), CollectionUtils.toStringMap("1", "a", "2", "b")));
+    }
+    
+    @Test
+    public void test_toMap() throws Exception {
+        assertTrue(CollectionUtils.toMap().isEmpty());
+        
+        
+        Map<String, Integer> expected = new HashMap<String, Integer>();
+        expected.put("a", 1);
+        expected.put("b", 2);
+        expected.put("c", 3);
+        
+        assertEquals(expected, CollectionUtils.toMap("a", 1, "b", 2, "c", 3));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtilsTest.java
new file mode 100644
index 0000000..6fe0b72
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtilsTest.java
@@ -0,0 +1,191 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertNull;

+import static org.junit.Assert.assertSame;

+import static org.junit.Assert.assertTrue;

+

+import java.math.BigDecimal;

+import java.math.BigInteger;

+import java.text.SimpleDateFormat;

+import java.util.ArrayList;

+import java.util.Date;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Set;

+import java.util.concurrent.CopyOnWriteArrayList;

+

+import org.junit.Test;

+
+public class CompatibleTypeUtilsTest {
+
+    @SuppressWarnings("unchecked")

+    @Test
+    public void testCompatibleTypeConvert() throws Exception {

+        Object result;

+        

+        {

+            Object input = new Object();

+            result = CompatibleTypeUtils.compatibleTypeConvert(input, Date.class);

+            assertSame(input, result);

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert(input, null);

+            assertSame(input, result);

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert(null, Date.class);

+            assertNull(result);

+        }

+        

+        {
+            result = CompatibleTypeUtils.compatibleTypeConvert("a", char.class);
+            assertEquals(Character.valueOf('a'), (Character) result);

+

+            result = CompatibleTypeUtils.compatibleTypeConvert("A", MyEnum.class);

+            assertEquals(MyEnum.A, (MyEnum) result);

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert("3", BigInteger.class);

+            assertEquals(new BigInteger("3"), (BigInteger) result);

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert("3", BigDecimal.class);

+            assertEquals(new BigDecimal("3"), (BigDecimal) result);

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11 12:24:12", Date.class);

+            assertEquals(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2011-12-11 12:24:12"), (Date) result);

+        }

+        

+        {

+            result = CompatibleTypeUtils.compatibleTypeConvert(3, byte.class);

+            assertEquals(Byte.valueOf((byte)3), (Byte) result);

+

+            result = CompatibleTypeUtils.compatibleTypeConvert((byte)3, int.class);

+            assertEquals(Integer.valueOf(3), (Integer) result);

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert(3, short.class);

+            assertEquals(Short.valueOf((short)3), (Short) result);

+

+            result = CompatibleTypeUtils.compatibleTypeConvert((short)3, int.class);

+            assertEquals(Integer.valueOf(3), (Integer) result);

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(3, int.class);

+            assertEquals(Integer.valueOf(3), (Integer) result);

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert(3, long.class);

+            assertEquals(Long.valueOf(3), (Long) result);

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert(3L, int.class);

+            assertEquals(Integer.valueOf(3), (Integer) result);

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(3L, BigInteger.class);

+            assertEquals(BigInteger.valueOf(3L), (BigInteger) result);

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(BigInteger.valueOf(3L), int.class);

+            assertEquals(Integer.valueOf(3), (Integer) result);

+        }

+        

+        {

+            result = CompatibleTypeUtils.compatibleTypeConvert(3D, float.class);

+            assertEquals(Float.valueOf(3), (Float) result);

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(3F, double.class);

+            assertEquals(Double.valueOf(3), (Double) result);

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert(3D, double.class);

+            assertEquals(Double.valueOf(3), (Double) result);

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(3D, BigDecimal.class);

+            assertEquals(BigDecimal.valueOf(3D), (BigDecimal) result);

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(BigDecimal.valueOf(3D), double.class);

+            assertEquals(Double.valueOf(3), (Double) result);

+        }

+        

+        {

+            List<String> list = new ArrayList<String>();

+            list.add("a");

+            list.add("b");

+            

+            Set<String> set = new HashSet<String>();

+            set.add("a");

+            set.add("b");

+

+            String[] array = new String[] {"a", "b"};

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(array, List.class);

+            assertEquals(ArrayList.class, result.getClass());

+            assertEquals(2, ((List<String>)result).size());

+            assertTrue(((List<String>)result).contains("a"));

+            assertTrue(((List<String>)result).contains("b"));

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(set, List.class);

+            assertEquals(ArrayList.class, result.getClass());

+            assertEquals(2, ((List<String>)result).size());

+            assertTrue(((List<String>)result).contains("a"));

+            assertTrue(((List<String>)result).contains("b"));

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(array, CopyOnWriteArrayList.class);

+            assertEquals(CopyOnWriteArrayList.class, result.getClass());

+            assertEquals(2, ((List<String>)result).size());

+            assertTrue(((List<String>)result).contains("a"));

+            assertTrue(((List<String>)result).contains("b"));

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(set, CopyOnWriteArrayList.class);

+            assertEquals(CopyOnWriteArrayList.class, result.getClass());

+            assertEquals(2, ((List<String>)result).size());

+            assertTrue(((List<String>)result).contains("a"));

+            assertTrue(((List<String>)result).contains("b"));

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(set, String[].class);

+            assertEquals(String[].class, result.getClass());

+            assertEquals(2, ((String[])result).length);

+            assertTrue(((String[])result)[0].equals("a") || ((String[])result)[0].equals("b"));

+            assertTrue(((String[])result)[1].equals("a") || ((String[])result)[1].equals("b"));

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(array, Set.class);

+            assertEquals(HashSet.class, result.getClass());

+            assertEquals(2, ((Set<String>)result).size());

+            assertTrue(((Set<String>)result).contains("a"));

+            assertTrue(((Set<String>)result).contains("b"));

+            

+            result = CompatibleTypeUtils.compatibleTypeConvert(list, Set.class);

+            assertEquals(HashSet.class, result.getClass());

+            assertEquals(2, ((Set<String>)result).size());

+            assertTrue(((Set<String>)result).contains("a"));

+            assertTrue(((Set<String>)result).contains("b"));

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(array, ConcurrentHashSet.class);

+            assertEquals(ConcurrentHashSet.class, result.getClass());

+            assertEquals(2, ((Set<String>)result).size());

+            assertTrue(((Set<String>)result).contains("a"));

+            assertTrue(((Set<String>)result).contains("b"));

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(list, ConcurrentHashSet.class);

+            assertEquals(ConcurrentHashSet.class, result.getClass());

+            assertEquals(2, ((Set<String>)result).size());

+            assertTrue(((Set<String>)result).contains("a"));

+            assertTrue(((Set<String>)result).contains("b"));

+

+            result = CompatibleTypeUtils.compatibleTypeConvert(list, String[].class);

+            assertEquals(String[].class, result.getClass());

+            assertEquals(2, ((String[])result).length);

+            assertTrue(((String[])result)[0].equals("a"));

+            assertTrue(((String[])result)[1].equals("b"));

+            

+        }

+        
+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ConfigUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ConfigUtilsTest.java
new file mode 100644
index 0000000..18e266e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ConfigUtilsTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.common.utils;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ * @author tony.chenl
+ */
+public class ConfigUtilsTest {
+
+    public static <T> List<T> toArray(T... args) {
+        List<T> ret = new ArrayList<T>();
+        for(T a : args) {
+            ret.add(a);
+        }
+        return ret;
+    }
+    
+    /**
+     * 测试点1:用户配置参数在最后 测试点2:用户配置参数如果带-,会删除同名的默认参数 测试点3:default开头的默认参数会被删除
+     */
+    @Test
+    public void testMergeValues() {
+        List<String> merged = ConfigUtils.mergeValues(Serialization.class, "aaa,bbb,default.cunstom",
+                toArray("dubbo","default.hessian2","json"));
+        Assert.assertEquals(toArray("dubbo", "json", "aaa", "bbb", "default.cunstom"), merged);
+    }
+    
+    /**
+     * 测试点1:用户配置参数在最后 测试点2:用户配置参数如果带-,会删除同名的默认参数 测试点3:default开头的默认参数会被删除
+     */
+    @Test
+    public void testMergeValues_addDefault() {
+        List<String> merged = ConfigUtils.mergeValues(Serialization.class, "aaa,bbb,default,zzz",
+                toArray("dubbo","default.hessian2","json"));
+        Assert.assertEquals(toArray("aaa", "bbb","dubbo", "json",  "zzz"), merged);
+    }
+    
+    /**
+     * 测试点1:用户配置-default,会删除所有默认参数
+     */
+    @Test
+    public void testMergeValuesDeleteDefault() {
+        List<String> merged = ConfigUtils.mergeValues(Serialization.class, "-default", toArray("dubbo","default.hessian2","json"));
+        Assert.assertEquals(toArray(), merged);
+    }
+    
+    /**
+     * 测试点1:用户配置-default,会删除所有默认参数
+     */
+    @Test
+    public void testMergeValuesDeleteDefault_2() {
+        List<String> merged = ConfigUtils.mergeValues(Serialization.class, "-default,aaa", toArray("dubbo","default.hessian2","json"));
+        Assert.assertEquals(toArray("aaa"), merged);
+    }
+    
+    /**
+     * 测试点1:用户配置-default,会删除所有默认参数
+     */
+    @Test
+    public void testMergeValuesDelete() {
+        List<String> merged = ConfigUtils.mergeValues(Serialization.class, "-dubbo,aaa", toArray("dubbo","default.hessian2","json"));
+        Assert.assertEquals(toArray("json", "aaa"), merged);
+    }
+    
+    @Test
+    public void test_loadProperties_noFile() throws Exception {
+        Properties p = ConfigUtils.loadProperties("notExisted", true);
+        Properties expected = new Properties();
+        Assert.assertEquals(expected, p);
+
+        p = ConfigUtils.loadProperties("notExisted", false);
+        Assert.assertEquals(expected, p);
+    }
+    
+    @Test
+    public void test_loadProperties_oneFile() throws Exception {
+        Properties p = ConfigUtils.loadProperties("properties.load", false);
+        
+        Properties expected = new Properties();
+        expected.put("a", "12");
+        expected.put("b", "34");
+        expected.put("c", "56");
+        
+        Assert.assertEquals(expected, p);
+    }
+    
+    @Test
+    public void test_loadProperties_oneFile_allowMulti() throws Exception {
+        Properties p = ConfigUtils.loadProperties("properties.load", true);
+        
+        Properties expected = new Properties();
+        expected.put("a", "12");
+        expected.put("b", "34");
+        expected.put("c", "56");
+        
+        Assert.assertEquals(expected, p);
+    }
+    
+    @Test
+    public void test_loadProperties_oneFile_notRootPath() throws Exception {
+        Properties p = ConfigUtils.loadProperties("META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool", false);
+        
+        Properties expected = new Properties();
+        expected.put("fixed", "com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool");
+        expected.put("cached", "com.alibaba.dubbo.common.threadpool.support.cached.CachedThreadPool");
+        
+        Assert.assertEquals(expected, p);
+    }
+    
+    
+    @Ignore("see http://code.alibabatech.com/jira/browse/DUBBO-133")
+    @Test
+    public void test_loadProperties_multiFile_notRootPath_Exception() throws Exception {
+        try {
+            ConfigUtils.loadProperties("META-INF/services/com.alibaba.dubbo.common.status.StatusChecker", false);
+            Assert.fail();
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString("only 1 META-INF/services/com.alibaba.dubbo.common.status.StatusChecker file is expected, but 2 dubbo.properties files found on class path:"));
+        }
+    }
+    
+    @Test
+    public void test_loadProperties_multiFile_notRootPath() throws Exception {
+        
+        Properties p = ConfigUtils.loadProperties("META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker", true);
+        
+        Properties expected = new Properties();
+        expected.put("memory", "com.alibaba.dubbo.common.status.support.MemoryStatusChecker");
+        expected.put("load", "com.alibaba.dubbo.common.status.support.LoadStatusChecker");
+        expected.put("aa", "12");
+        
+        Assert.assertEquals(expected, p);
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/MyEnum.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/MyEnum.java
new file mode 100644
index 0000000..1eb96ce
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/MyEnum.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+/**

+ * MyEnum

+ * 

+ * @author william.liangf

+ */

+public enum MyEnum {

+    

+    A, B

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/NetUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/NetUtilsTest.java
new file mode 100644
index 0000000..fcf12f3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/NetUtilsTest.java
@@ -0,0 +1,29 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import junit.framework.Assert;

+import junit.framework.TestCase;

+

+public class NetUtilsTest extends TestCase {

+	

+	public void testValidAddress() throws Exception {

+		Assert.assertTrue(NetUtils.isValidAddress("10.20.130.230:20880"));

+		Assert.assertFalse(NetUtils.isValidAddress("10.20.130.230"));

+		Assert.assertFalse(NetUtils.isValidAddress("10.20.130.230:666666"));

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ParametersTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ParametersTest.java
new file mode 100644
index 0000000..ae006af
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ParametersTest.java
@@ -0,0 +1,49 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class ParametersTest extends TestCase
+{
+	final String ServiceName = "com.alibaba.dubbo.rpc.service.GenericService";
+	final String ServiceVersion = "1.0.15";
+	final String LoadBalance = "lcr";
+
+	public void testMap2Parameters() throws Exception
+	{
+		Map<String, String> map = new HashMap<String, String>();
+		map.put("name", "com.alibaba.dubbo.rpc.service.GenericService");
+		map.put("version", "1.0.15");
+		map.put("lb", "lcr");
+		map.put("max.active", "500");
+		assertEquals(map.get("name"), ServiceName);
+		assertEquals(map.get("version"), ServiceVersion);
+		assertEquals(map.get("lb"), LoadBalance);
+	}
+
+	public void testString2Parameters() throws Exception
+	{
+		String qs = "name=com.alibaba.dubbo.rpc.service.GenericService&version=1.0.15&lb=lcr";
+		Map<String, String> map = StringUtils.parseQueryString(qs);
+		assertEquals(map.get("name"), ServiceName);
+		assertEquals(map.get("version"), ServiceVersion);
+		assertEquals(map.get("lb"), LoadBalance);
+	}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/PojoUtilsTest.java
new file mode 100644
index 0000000..e4f669e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/PojoUtilsTest.java
@@ -0,0 +1,259 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.model.Person;
+import com.alibaba.dubbo.common.model.SerializablePerson;
+import com.alibaba.dubbo.common.model.person.BigPerson;
+import com.alibaba.dubbo.common.model.person.FullAddress;
+import com.alibaba.dubbo.common.model.person.PersonInfo;
+import com.alibaba.dubbo.common.model.person.PersonStatus;
+import com.alibaba.dubbo.common.model.person.Phone;
+
+/**
+ * @author ding.lid
+ */
+public class PojoUtilsTest {
+    
+    public void assertObject(Object data) {
+        assertObject(data, null);
+    }
+    
+    public void assertObject(Object data, Type type) {
+        Object generalize = PojoUtils.generalize(data);
+        Object realize = PojoUtils.realize(generalize, data.getClass(), type);
+        assertEquals(data, realize);
+    }
+    
+    public <T> void assertArrayObject(T[] data) {
+        Object generalize = PojoUtils.generalize(data);
+        @SuppressWarnings("unchecked")
+        T[] realize = (T[]) PojoUtils.realize(generalize, data.getClass());
+        assertArrayEquals(data, realize);
+    }
+
+    @Test
+    public void test_primitive() throws Exception {
+        assertObject(Boolean.TRUE);
+        assertObject(Boolean.FALSE);
+
+        assertObject(Byte.valueOf((byte) 78));
+
+        assertObject('a');
+        assertObject('中');
+
+        assertObject(Short.valueOf((short) 37));
+
+        assertObject(78);
+
+        assertObject(123456789L);
+
+        assertObject(3.14F);
+        assertObject(3.14D);
+    }
+
+    @Test
+    public void test_pojo() throws Exception {
+        assertObject(new Person());
+        assertObject(new SerializablePerson());
+    }
+
+    @Test
+    public void test_PrimitiveArray() throws Exception {
+        assertObject(new boolean[] { true, false });
+        assertObject(new Boolean[] { true, false, true });
+
+        assertObject(new byte[] { 1, 12, 28, 78 });
+        assertObject(new Byte[] { 1, 12, 28, 78 });
+
+        assertObject(new char[] { 'a', '中', '无' });
+        assertObject(new Character[] { 'a', '中', '无' });
+
+        assertObject(new short[] { 37, 39, 12 });
+        assertObject(new Short[] { 37, 39, 12 });
+
+        assertObject(new int[] { 37, -39, 12456 });
+        assertObject(new Integer[] { 37, -39, 12456 });
+        
+        assertObject(new long[] { 37L, -39L, 123456789L });
+        assertObject(new Long[] { 37L, -39L, 123456789L });
+
+        assertObject(new float[] { 37F, -3.14F, 123456.7F });
+        assertObject(new Float[] { 37F, -39F, 123456.7F });
+        
+        assertObject(new double[] { 37D, -3.14D, 123456.7D });
+        assertObject(new Double[] { 37D, -39D, 123456.7D});
+        
+
+        assertArrayObject(new Boolean[] { true, false, true });
+
+        assertArrayObject(new Byte[] { 1, 12, 28, 78 });
+
+        assertArrayObject(new Character[] { 'a', '中', '无' });
+
+        assertArrayObject(new Short[] { 37, 39, 12 });
+
+        assertArrayObject(new Integer[] { 37, -39, 12456 });
+        
+        assertArrayObject(new Long[] { 37L, -39L, 123456789L });
+
+        assertArrayObject(new Float[] { 37F, -39F, 123456.7F });
+        
+        assertArrayObject(new Double[] { 37D, -39D, 123456.7D});
+    }
+
+    @Test
+    public void test_PojoArray() throws Exception {
+        Person[] array = new Person[2];
+        array[0] = new Person();
+        {
+            Person person = new Person();
+            person.setName("xxxx");
+            array[1] = person;
+        }
+        assertArrayObject(array);
+    }
+
+    public List<Person> returnListPersonMethod() {return null;}
+    public BigPerson returnBigPersonMethod() {return null;}
+    public Type getType(String methodName){
+        Method method;
+        try {
+            method = getClass().getDeclaredMethod(methodName, new Class<?>[]{} );
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+        Type gtype = method.getGenericReturnType();
+        return gtype;
+    }
+    
+    @Test
+    public void test_simpleCollection() throws Exception {
+        Type gtype = getType("returnListPersonMethod");
+        List<Person> list = new ArrayList<Person>();
+        list.add(new Person());
+        {
+            Person person = new Person();
+            person.setName("xxxx");
+            list.add(person);
+        }
+        assertObject(list,gtype);
+    }
+
+    
+    BigPerson bigPerson;
+    {
+        bigPerson = new BigPerson();
+        bigPerson.setPersonId("id1");
+        bigPerson.setLoginName("name1");
+        bigPerson.setStatus(PersonStatus.ENABLED);
+        bigPerson.setEmail("abc@123.com");
+        bigPerson.setPenName("pname");
+
+        ArrayList<Phone> phones = new ArrayList<Phone>();
+        Phone phone1 = new Phone("86", "0571", "11223344", "001");
+        Phone phone2 = new Phone("86", "0571", "11223344", "002");
+        phones.add(phone1);
+        phones.add(phone2);
+
+        PersonInfo pi = new PersonInfo();
+        pi.setPhones(phones);
+        Phone fax = new Phone("86", "0571", "11223344", null);
+        pi.setFax(fax);
+        FullAddress addr = new FullAddress("CN", "zj", "1234", "Road1", "333444");
+        pi.setFullAddress(addr);
+        pi.setMobileNo("1122334455");
+        pi.setMale(true);
+        pi.setDepartment("b2b");
+        pi.setHomepageUrl("www.abc.com");
+        pi.setJobTitle("dev");
+        pi.setName("name2");
+
+        bigPerson.setInfoProfile(pi);
+    }
+
+    @Test
+    public void test_total() throws Exception {
+        Object generalize = PojoUtils.generalize(bigPerson);
+        Type gtype = getType("returnBigPersonMethod");
+        Object realize = PojoUtils.realize(generalize, BigPerson.class,gtype);
+        assertEquals(bigPerson, realize);
+    }
+
+    @Test
+    public void test_total_Array() throws Exception {
+        Object[] persons = new Object[] { bigPerson, bigPerson, bigPerson };
+
+        Object generalize = PojoUtils.generalize(persons);
+        Object[] realize = (Object[]) PojoUtils.realize(generalize, Object[].class);
+        assertArrayEquals(persons, realize);
+    }
+
+    public static <T extends Comparable<T>> T min(T[] arr) {
+        if (arr == null || arr.length == 0)
+            return null;
+
+        T smallest = arr[0];
+        for (int i = 1; i < arr.length; ++i) {
+            if (smallest.compareTo(arr[i]) > 0) {
+                smallest = arr[i];
+            }
+        }
+        return smallest;
+    }
+
+    public static <T extends Comparable<? super T>> T min2(T[] arr) {
+        if (arr == null || arr.length == 0)
+            return null;
+
+        T smallest = arr[0];
+        for (int i = 1; i < arr.length; ++i) {
+            if (smallest.compareTo(arr[i]) > 0) {
+                smallest = arr[i];
+            }
+        }
+        return smallest;
+    }
+
+    public static <T extends Comparable<T> & Serializable> T max(T[] arr) {
+        if (arr == null || arr.length == 0)
+            return null;
+
+        T biggest = arr[0];
+        for (int i = 1; i < arr.length; ++i) {
+            if (biggest.compareTo(arr[i]) < 0) {
+                biggest = arr[i];
+            }
+        }
+        return biggest;
+    }
+
+    public static <T extends Comparable<T> & Serializable> T max2(Comparable<? extends Serializable>[] arr) {
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ReflectUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ReflectUtilsTest.java
new file mode 100644
index 0000000..e811154
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ReflectUtilsTest.java
@@ -0,0 +1,172 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import static org.junit.Assert.assertThat;

+import static org.junit.matchers.JUnitMatchers.containsString;

+

+import java.lang.reflect.Method;

+import java.util.ArrayList;

+import java.util.List;

+

+import junit.framework.TestCase;

+

+import org.junit.Test;

+
+public class ReflectUtilsTest extends TestCase
+{
+	public void testIsCompatible() throws Exception
+	{
+		assertEquals(ReflectUtils.isCompatible(short.class, (short)1), true);
+		assertEquals(ReflectUtils.isCompatible(int.class, 1), true);
+		assertEquals(ReflectUtils.isCompatible(double.class, 1.2), true);
+		assertEquals(ReflectUtils.isCompatible(Object.class, 1.2), true);
+		assertEquals(ReflectUtils.isCompatible(List.class, new ArrayList<String>()), true);
+	}
+
+	public void testNameDesc() throws Exception
+	{
+		// getName
+		assertEquals("boolean", ReflectUtils.getName(boolean.class));
+		assertEquals("int[][][]", ReflectUtils.getName(int[][][].class));
+		assertEquals("java.lang.Object[][]", ReflectUtils.getName(Object[][].class));
+
+		// getDesc
+		assertEquals("Z", ReflectUtils.getDesc(boolean.class));
+		assertEquals("[[[I", ReflectUtils.getDesc(int[][][].class));
+		assertEquals("[[Ljava/lang/Object;", ReflectUtils.getDesc(Object[][].class));
+
+		// name2desc
+		assertEquals("Z", ReflectUtils.name2desc(ReflectUtils.getName(boolean.class)));
+		assertEquals("[[[I", ReflectUtils.name2desc(ReflectUtils.getName(int[][][].class)));
+		assertEquals("[[Ljava/lang/Object;", ReflectUtils.name2desc(ReflectUtils.getName(Object[][].class)));
+
+		// desc2name
+		assertEquals("short[]", ReflectUtils.desc2name(ReflectUtils.getDesc(short[].class)));
+		assertEquals("int", ReflectUtils.desc2name(ReflectUtils.getDesc(int.class)));
+		assertEquals("java.lang.Object[][]", ReflectUtils.desc2name(ReflectUtils.getDesc(Object[][].class)));
+	}
+
+	public void testName2Class() throws Exception
+	{
+		assertEquals(boolean.class, ReflectUtils.name2class("boolean"));
+		assertEquals(boolean[].class, ReflectUtils.name2class("boolean[]"));
+		assertEquals(int[][].class, ReflectUtils.name2class(ReflectUtils.getName(int[][].class)));
+		assertEquals(ReflectUtilsTest[].class, ReflectUtils.name2class(ReflectUtils.getName(ReflectUtilsTest[].class)));
+	}
+
+	public void testDesc2Class() throws Exception
+	{
+		assertEquals(boolean.class, ReflectUtils.desc2class("Z"));
+		assertEquals(boolean[].class, ReflectUtils.desc2class("[Z"));
+		assertEquals(int[][].class, ReflectUtils.desc2class(ReflectUtils.getDesc(int[][].class)));
+		assertEquals(ReflectUtilsTest[].class, ReflectUtils.desc2class(ReflectUtils.getDesc(ReflectUtilsTest[].class)));
+
+		String desc;
+		Class<?>[] cs;
+
+		cs = new Class<?>[]{ int.class, getClass(), String.class, int[][].class, boolean[].class };
+		desc = ReflectUtils.getDesc(cs);
+		assertSame(cs, ReflectUtils.desc2classArray(desc));
+
+		cs = new Class<?>[]{};
+		desc = ReflectUtils.getDesc(cs);
+		assertSame(cs, ReflectUtils.desc2classArray(desc));
+
+		cs = new Class<?>[]{ void.class, String[].class, int[][].class, ReflectUtilsTest[][].class };
+		desc = ReflectUtils.getDesc(cs);
+		assertSame(cs, ReflectUtils.desc2classArray(desc));
+	}
+
+	protected void assertSame(Class<?>[] cs1, Class<?>[] cs2) throws Exception
+	{
+		assertEquals(cs1.length, cs2.length);
+		for(int i=0;i<cs1.length;i++)
+			assertEquals(cs1[i], cs2[i]);
+	}
+	
+    @Test
+    public void test_findMethodByMethodSignature() throws Exception {
+        Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class, "method1", null);
+
+        assertEquals("method1", m.getName());
+        Class<?>[] parameterTypes = m.getParameterTypes();
+        assertEquals(1, parameterTypes.length);
+        assertEquals(int.class, parameterTypes[0]);
+    }
+
+    @Test
+    public void test_findMethodByMethodSignature_override() throws Exception {
+        {
+            Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class,
+                    "overrideMethod", new String[] {"int"});
+
+            assertEquals("overrideMethod", m.getName());
+            Class<?>[] parameterTypes = m.getParameterTypes();
+            assertEquals(1, parameterTypes.length);
+            assertEquals(int.class, parameterTypes[0]);
+        }
+        {
+            Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class,
+                    "overrideMethod", new String[] {"java.lang.Integer"});
+
+            assertEquals("overrideMethod", m.getName());
+            Class<?>[] parameterTypes = m.getParameterTypes();
+            assertEquals(1, parameterTypes.length);
+            assertEquals(Integer.class, parameterTypes[0]);
+        }
+    }
+
+    @Test
+    public void test_findMethodByMethodSignature_override_Morethan1() throws Exception {
+        try {
+        	ReflectUtils.findMethodByMethodSignature(TestedClass.class, "overrideMethod", null);
+            fail();
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString(
+                    "Not unique method for method name("));
+        }
+    }
+
+    @Test
+    public void test_findMethodByMethodSignature_notFound() throws Exception {
+        try {
+        	ReflectUtils.findMethodByMethodSignature(TestedClass.class, "notExsited", null);
+            fail();
+        } catch (NoSuchMethodException expected) {
+            assertThat(expected.getMessage(), containsString("No such method "));
+            assertThat(expected.getMessage(), containsString("in class"));
+        }
+    }
+
+    static class TestedClass {
+        public void method1(int x) {
+        }
+
+        public void overrideMethod(int x) {
+        }
+
+        public void overrideMethod(Integer x) {
+        }
+
+        public void overrideMethod(String s) {
+        }
+
+        public void overrideMethod(String s1, String s2) {
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.java
new file mode 100644
index 0000000..da816f6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.java
@@ -0,0 +1,69 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;
+
+import java.util.ArrayList;

+import java.util.List;

+

+import junit.framework.TestCase;

+

+import org.junit.Test;

+
+public class StringUtilsTest extends TestCase
+{
+	public void testJoin() throws Exception
+	{
+		String[] s = {"1","2","3"};
+		assertEquals(StringUtils.join(s), "123");
+		assertEquals(StringUtils.join(s, ','), "1,2,3");
+	}
+
+	public void testSplit() throws Exception
+	{
+		String s = "d,1,2,4";
+		assertEquals(StringUtils.split(s, ',').length, 4);
+	}
+
+	public void testTranslat() throws Exception
+	{
+		String s = "16314";
+		assertEquals(StringUtils.translat(s, "123456", "abcdef"), "afcad");
+		assertEquals(StringUtils.translat(s, "123456", "abcd"), "acad");
+	}
+
+	public void testJoin_Colletion_String() throws Exception
+	{
+	    List<String> list = new ArrayList<String>();
+	    assertEquals("", StringUtils.join(list, ","));
+	    
+	    list.add("v1");
+        assertEquals("v1", StringUtils.join(list, "-"));
+        
+	    list.add("v2");
+	    list.add("v3");
+	    String out = StringUtils.join(list, ":");
+	    assertEquals("v1:v2:v3", out);
+	}

+	

+	@Test

+	public void testCamelToSplitName() throws Exception

+    {

+	    assertEquals("ab-cd-ef", StringUtils.camelToSplitName("abCdEf", "-"));

+        assertEquals("ab-cd-ef", StringUtils.camelToSplitName("AbCdEf", "-"));

+        assertEquals("ab-cd-ef", StringUtils.camelToSplitName("ab-cd-ef", "-"));

+        assertEquals("abcdef", StringUtils.camelToSplitName("abcdef", "-"));

+    }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/UrlUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/UrlUtilsTest.java
new file mode 100644
index 0000000..83bde30
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/UrlUtilsTest.java
@@ -0,0 +1,311 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.common.utils;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertNull;

+import static org.junit.Assert.assertTrue;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * @author tony.chenl

+ */

+public class UrlUtilsTest {

+

+    String localAddress = NetUtils.getLocalHost();

+

+    @Test

+    public void testAddressNull() {

+        assertNull(UrlUtils.parseURL(null, null));

+    }

+

+    @Test

+    public void testParseUrl() {

+        String address = "remote://root:alibaba@127.0.0.1:9090/dubbo.test.api";

+        URL url = UrlUtils.parseURL(address, null);

+        assertEquals(localAddress + ":9090", url.getAddress());

+        assertEquals("root", url.getUsername());

+        assertEquals("alibaba", url.getPassword());

+        assertEquals("dubbo.test.api", url.getPath());

+        assertEquals(9090, url.getPort());

+        assertEquals("remote", url.getProtocol());

+    }

+

+    @Test

+    public void testDefaultUrl() {

+        String address = "127.0.0.1";

+        URL url = UrlUtils.parseURL(address, null);

+        assertEquals(localAddress + ":9090", url.getAddress());

+        assertEquals(9090, url.getPort());

+        assertEquals("dubbo", url.getProtocol());

+        assertNull(url.getUsername());

+        assertNull(url.getPassword());

+        assertNull(url.getPath());

+    }

+

+    @Test

+    public void testParseFromParameter() {

+        String address = "127.0.0.1";

+        Map<String, String> parameters = new HashMap<String, String>();

+        parameters.put("username", "root");

+        parameters.put("password", "alibaba");

+        parameters.put("port", "10000");

+        parameters.put("protocol", "dubbo");

+        parameters.put("path", "dubbo.test.api");

+        parameters.put("aaa", "bbb");

+        parameters.put("ccc", "ddd");

+        URL url = UrlUtils.parseURL(address, parameters);

+        assertEquals(localAddress + ":10000", url.getAddress());

+        assertEquals("root", url.getUsername());

+        assertEquals("alibaba", url.getPassword());

+        assertEquals(10000, url.getPort());

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals("dubbo.test.api", url.getPath());

+        assertEquals("bbb", url.getParameter("aaa"));

+        assertEquals("ddd", url.getParameter("ccc"));

+    }

+

+    @Test

+    public void testParseUrl2() {

+        String address = "127.0.0.1";

+        String backupAddress1 = "127.0.0.2";

+        String backupAddress2 = "127.0.0.3";

+

+        Map<String, String> parameters = new HashMap<String, String>();

+        parameters.put("username", "root");

+        parameters.put("password", "alibaba");

+        parameters.put("port", "10000");

+        parameters.put("protocol", "dubbo");

+        URL url = UrlUtils.parseURL(address + "," + backupAddress1 + "," + backupAddress2, parameters);

+        assertEquals(localAddress + ":10000", url.getAddress());

+        assertEquals("root", url.getUsername());

+        assertEquals("alibaba", url.getPassword());

+        assertEquals(10000, url.getPort());

+        assertEquals("dubbo", url.getProtocol());

+        assertEquals(localAddress + "," + localAddress, url.getParameter("backup"));

+    }

+

+    @Test

+    public void testParseUrls() {

+        String addresses = "127.0.0.1|127.0.0.2|127.0.0.3";

+        Map<String, String> parameters = new HashMap<String, String>();

+        parameters.put("username", "root");

+        parameters.put("password", "alibaba");

+        parameters.put("port", "10000");

+        parameters.put("protocol", "dubbo");

+        List<URL> urls = UrlUtils.parseURLs(addresses, parameters);

+        assertEquals(localAddress + ":10000", urls.get(0).getAddress());

+        assertEquals(localAddress + ":10000", urls.get(1).getAddress());

+    }

+

+    @Test

+    public void testParseUrlsAddressNull() {

+        assertNull(UrlUtils.parseURLs(null, null));

+    }

+

+    @Test

+    public void testConvertRegister() {

+        String key = "perf/dubbo.test.api.HelloService:1.0.0";

+        Map<String, Map<String, String>> register = new HashMap<String, Map<String, String>>();

+        register.put(key, null);

+        Map<String, Map<String, String>> newRegister = UrlUtils.convertRegister(register);

+        assertEquals(register, newRegister);

+    }

+

+    @Test

+    public void testConvertRegister2() {

+        String key = "dubbo.test.api.HelloService";

+        Map<String, Map<String, String>> register = new HashMap<String, Map<String, String>>();

+        Map<String, String> service = new HashMap<String, String>();

+        service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "version=1.0.0&group=test&dubbo.version=2.0.0");

+        register.put(key, service);

+        Map<String, Map<String, String>> newRegister = UrlUtils.convertRegister(register);

+        Map<String, String> newService = new HashMap<String, String>();

+        newService.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "dubbo.version=2.0.0");

+        assertEquals(newService, newRegister.get("test/dubbo.test.api.HelloService:1.0.0"));

+    }

+

+    @Test

+    public void testSubscribe() {

+        String key = "perf/dubbo.test.api.HelloService:1.0.0";

+        Map<String, String> subscribe = new HashMap<String, String>();

+        subscribe.put(key, null);

+        Map<String, String> newSubscribe = UrlUtils.convertSubscribe(subscribe);

+        assertEquals(subscribe, newSubscribe);

+    }

+

+    @Test

+    public void testSubscribe2() {

+        String key = "dubbo.test.api.HelloService";

+        Map<String, String> subscribe = new HashMap<String, String>();

+        subscribe.put(key, "version=1.0.0&group=test&dubbo.version=2.0.0");

+        Map<String, String> newSubscribe = UrlUtils.convertSubscribe(subscribe);

+        assertEquals("dubbo.version=2.0.0", newSubscribe.get("test/dubbo.test.api.HelloService:1.0.0"));

+    }

+

+    @Test

+    public void testRevertRegister() {

+        String key = "perf/dubbo.test.api.HelloService:1.0.0";

+        Map<String, Map<String, String>> register = new HashMap<String, Map<String, String>>();

+        Map<String, String> service = new HashMap<String, String>();

+        service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", null);

+        register.put(key, service);

+        Map<String, Map<String, String>> newRegister = UrlUtils.revertRegister(register);

+        Map<String, Map<String, String>> expectedRegister = new HashMap<String, Map<String, String>>();

+        service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0");

+        expectedRegister.put("dubbo.test.api.HelloService", service);

+        assertEquals(expectedRegister, newRegister);

+    }

+

+    @Test

+    public void testRevertRegister2() {

+        String key = "dubbo.test.api.HelloService";

+        Map<String, Map<String, String>> register = new HashMap<String, Map<String, String>>();

+        Map<String, String> service = new HashMap<String, String>();

+        service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", null);

+        register.put(key, service);

+        Map<String, Map<String, String>> newRegister = UrlUtils.revertRegister(register);

+        Map<String, Map<String, String>> expectedRegister = new HashMap<String, Map<String, String>>();

+        service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", null);

+        expectedRegister.put("dubbo.test.api.HelloService", service);

+        assertEquals(expectedRegister, newRegister);

+    }

+

+    @Test

+    public void testRevertSubscribe() {

+        String key = "perf/dubbo.test.api.HelloService:1.0.0";

+        Map<String, String> subscribe = new HashMap<String, String>();

+        subscribe.put(key, null);

+        Map<String, String> newSubscribe = UrlUtils.revertSubscribe(subscribe);

+        Map<String, String> expectSubscribe = new HashMap<String, String>();

+        expectSubscribe.put("dubbo.test.api.HelloService", "group=perf&version=1.0.0");

+        assertEquals(expectSubscribe, newSubscribe);

+    }

+

+    @Test

+    public void testRevertSubscribe2() {

+        String key = "dubbo.test.api.HelloService";

+        Map<String, String> subscribe = new HashMap<String, String>();

+        subscribe.put(key, null);

+        Map<String, String> newSubscribe = UrlUtils.revertSubscribe(subscribe);

+        assertEquals(subscribe, newSubscribe);

+    }

+

+    @Test

+    public void testRevertNotify() {

+        String key = "dubbo.test.api.HelloService";

+        Map<String, Map<String, String>> notify = new HashMap<String, Map<String, String>>();

+        Map<String, String> service = new HashMap<String, String>();

+        service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0");

+        notify.put(key, service);

+        Map<String, Map<String, String>> newRegister = UrlUtils.revertNotify(notify);

+        Map<String, Map<String, String>> expectedRegister = new HashMap<String, Map<String, String>>();

+        service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0");

+        expectedRegister.put("perf/dubbo.test.api.HelloService:1.0.0", service);

+        assertEquals(expectedRegister, newRegister);

+    }

+

+    @Test

+    public void testRevertNotify2() {

+        String key = "perf/dubbo.test.api.HelloService:1.0.0";

+        Map<String, Map<String, String>> notify = new HashMap<String, Map<String, String>>();

+        Map<String, String> service = new HashMap<String, String>();

+        service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0");

+        notify.put(key, service);

+        Map<String, Map<String, String>> newRegister = UrlUtils.revertNotify(notify);

+        Map<String, Map<String, String>> expectedRegister = new HashMap<String, Map<String, String>>();

+        service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0");

+        expectedRegister.put("perf/dubbo.test.api.HelloService:1.0.0", service);

+        assertEquals(expectedRegister, newRegister);

+    }

+

+    // 为了兼容2.0.0版本的rpc

+    @Test

+    public void testRevertForbid() {

+        String service = "dubbo.test.api.HelloService";

+        List<String> forbid = new ArrayList<String>();

+        forbid.add(service);

+        Set<String> subscribed = new HashSet<String>();

+        subscribed.add("perf/+" + service + ":1.0.0");

+        List<String> newForbid = UrlUtils.revertForbid(forbid, subscribed);

+        List<String> expectForbid = new ArrayList<String>();

+        expectForbid.add("perf/+" + service + ":1.0.0");

+        assertEquals(expectForbid, newForbid);

+    }

+

+    @Test

+    public void testRevertForbid2() {

+        List<String> newForbid = UrlUtils.revertForbid(null, null);

+        assertNull(newForbid);

+    }

+

+    @Test

+    public void testRevertForbid3() {

+        String service1 = "dubbo.test.api.HelloService:1.0.0";

+        String service2 = "dubbo.test.api.HelloService:2.0.0";

+        List<String> forbid = new ArrayList<String>();

+        forbid.add(service1);

+        forbid.add(service2);

+        List<String> newForbid = UrlUtils.revertForbid(forbid, null);

+        assertEquals(forbid, newForbid);

+    }

+

+    @Test

+    public void testIsMatch() {

+        URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=test");

+        URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");

+        assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl));

+    }

+

+    @Test

+    public void testIsMatch2() {

+        URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=2.0.0&group=test");

+        URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");

+        assertFalse(UrlUtils.isMatch(consumerUrl, providerUrl));

+    }

+

+    @Test

+    public void testIsMatch3() {

+        URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=aa");

+        URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");

+        assertFalse(UrlUtils.isMatch(consumerUrl, providerUrl));

+    }

+

+    @Test

+    public void testIsMatch4() {

+        URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=*");

+        URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");

+        assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl));

+    }

+

+    @Test

+    public void testIsMatch5() {

+        URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=*&group=test");

+        URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");

+        assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl));

+    }

+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1
new file mode 100644
index 0000000..8ff8934
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1
@@ -0,0 +1,4 @@
+group=com.alibaba.dubbo.common.extensionloader.activate.impl.GroupActivateExtImpl
+value=com.alibaba.dubbo.common.extensionloader.activate.impl.ValueActivateExtImpl
+order1=com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl1
+order2=com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext1.Ext1 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext1.Ext1
new file mode 100644
index 0000000..ea1ce31
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext1.Ext1
@@ -0,0 +1,4 @@
+# Comment 1

+impl1=com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl1#Hello World

+impl2=com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl2  # Comment 2

+   impl3=com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl3 # with head space
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext2.Ext2 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext2.Ext2
new file mode 100644
index 0000000..e275f40
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext2.Ext2
@@ -0,0 +1,3 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl1

+impl2=com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl2

+impl3=com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl3
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext3.Ext3 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext3.Ext3
new file mode 100644
index 0000000..62bfa83
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext3.Ext3
@@ -0,0 +1,3 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl1

+impl2=com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl2

+impl3=com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl3
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext4.Ext4 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext4.Ext4
new file mode 100644
index 0000000..3c64e22
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext4.Ext4
@@ -0,0 +1,2 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext4.impl.Ext4Impl1

+impl2=com.alibaba.dubbo.common.extensionloader.ext4.impl.Ext4Impl2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod
new file mode 100644
index 0000000..5604232
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod
@@ -0,0 +1,4 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Impl1

+impl2=com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Impl2

+wrapper1=com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper1

+wrapper2=com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6
new file mode 100644
index 0000000..885aef5
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6
@@ -0,0 +1,2 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl1

+impl2=com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext7.Ext7 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext7.Ext7
new file mode 100644
index 0000000..5fa7829
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext7.Ext7
@@ -0,0 +1,2 @@
+error=com.alibaba.dubbo.common.extensionloader.ext7.impl.Ext7InitErrorImpl

+ok=com.alibaba.dubbo.common.extensionloader.ext7.impl.Ext7Impl

diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..82d8158
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1 @@
+aa=12
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1
new file mode 100644
index 0000000..2e16c11
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1
@@ -0,0 +1 @@
+com.alibaba.dubbo.common.extensionloader.activate.impl.ActivateExt1Impl1
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/StreamUtilsTest.txt b/dubbo-common/src/test/resources/StreamUtilsTest.txt
new file mode 100644
index 0000000..ad47100
--- /dev/null
+++ b/dubbo-common/src/test/resources/StreamUtilsTest.txt
@@ -0,0 +1 @@
+0123456789
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.fc b/dubbo-common/src/test/resources/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.fc
new file mode 100644
index 0000000..c8c083c
--- /dev/null
+++ b/dubbo-common/src/test/resources/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.fc
@@ -0,0 +1,2 @@
+a,d,e,b,c
+str3,str2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/json.flex b/dubbo-common/src/test/resources/json.flex
new file mode 100644
index 0000000..c6d4015
--- /dev/null
+++ b/dubbo-common/src/test/resources/json.flex
@@ -0,0 +1,68 @@
+package com.alibaba.dubbo.common.json;
+%%
+
+%{
+private StringBuffer sb;
+%}
+
+%table
+%unicode
+%state STR1,STR2
+
+%yylexthrow ParseException
+
+HEX = [a-fA-F0-9]
+HEX4 = {HEX}{HEX}{HEX}{HEX}
+
+IDENT = [a-zA-Z_$] [a-zA-Z0-9_$]*
+INT_LITERAL = [-]? [0-9]+
+FLOAT_LITERAL = {INT_LITERAL} ( ( \.[0-9]+ ) ? ( [eE][-+]? [0-9]+ )? )
+
+ESC1 = [^\"\\]
+ESC2 = [^\'\\]
+
+SKIP = [ \t\r\n]
+OTHERS = .
+%%
+
+<STR1>{
+	\"				{ yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString()); }
+	{ESC1}+			{ sb.append(yytext()); }
+	\\\"			{ sb.append('"'); }
+}
+
+<STR2>{
+	\'				{ yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString()); }
+	{ESC2}+			{ sb.append(yytext()); }
+	\\\'			{ sb.append('\''); }
+}
+
+<STR1,STR2>{
+	\\\\			{ sb.append('\\'); }
+	\\\/			{ sb.append('/'); }
+	\\b				{ sb.append('\b'); }
+	\\f				{ sb.append('\f'); }
+	\\n				{ sb.append('\n'); }
+	\\r				{ sb.append('\r'); }
+	\\t				{ sb.append('\t'); }
+	\\u{HEX4}		{ try{ sb.append((char)Integer.parseInt(yytext().substring(2),16)); }catch(Exception e){ throw new ParseException(e.getMessage()); } }
+}
+
+<YYINITIAL>{
+	\"					{ sb = new StringBuffer(); yybegin(STR1); }
+	\'					{ sb = new StringBuffer(); yybegin(STR2); }
+	{INT_LITERAL}		{ Long val = Long.valueOf(yytext()); return new JSONToken(JSONToken.INT, val); }
+	{FLOAT_LITERAL}		{ Double val = Double.valueOf(yytext()); return new JSONToken(JSONToken.FLOAT, val); }
+	"true"|"TRUE"		{ return new JSONToken(JSONToken.BOOL, Boolean.TRUE); }
+	"false"|"FALSE"		{ return new JSONToken(JSONToken.BOOL, Boolean.FALSE); }
+	"null"|"NULL"		{ return new JSONToken(JSONToken.NULL, null); }
+	{IDENT}				{ return new JSONToken(JSONToken.IDENT, yytext()); }
+	"{"					{ return new JSONToken(JSONToken.LBRACE); }
+	"}"					{ return new JSONToken(JSONToken.RBRACE); }
+	"["					{ return new JSONToken(JSONToken.LSQUARE); }
+	"]"					{ return new JSONToken(JSONToken.RSQUARE); }
+	","					{ return new JSONToken(JSONToken.COMMA); }
+	":"					{ return new JSONToken(JSONToken.COLON); }
+	{SKIP}+ 			{}
+	{OTHERS} 			{ throw new ParseException("Unexpected char [" + yytext() +"]"); }
+}
diff --git a/dubbo-common/src/test/resources/log4j.xml b/dubbo-common/src/test/resources/log4j.xml
new file mode 100644
index 0000000..a09e14d
--- /dev/null
+++ b/dubbo-common/src/test/resources/log4j.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+	<!-- ===================================================================== -->
+	<!-- 以下是appender的定义 -->
+	<!-- ===================================================================== -->
+	<appender name="dubbo" class="com.alibaba.dubbo.common.utils.DubboAppender">
+		<param name="encoding" value="GBK" />
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="%d %p [%c:%M] - %m%n" />
+		</layout>
+	</appender>

+	<root>
+		<level value="INFO" />
+		<appender-ref ref="dubbo" />

+	</root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/properties.load b/dubbo-common/src/test/resources/properties.load
new file mode 100644
index 0000000..21bb7f2
--- /dev/null
+++ b/dubbo-common/src/test/resources/properties.load
@@ -0,0 +1,3 @@
+a=12

+b=34

+c=56
\ No newline at end of file
diff --git a/dubbo-config/pom.xml b/dubbo-config/pom.xml
new file mode 100644
index 0000000..d3d2147
--- /dev/null
+++ b/dubbo-config/pom.xml
@@ -0,0 +1,125 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-config</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Config Module</name>
+	<description>The config module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-registry</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-monitor</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-validation</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-cache</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-registry-default</artifactId>
+			<version>${project.parent.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-monitor-default</artifactId>
+			<version>${project.parent.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-rpc-default</artifactId>
+			<version>${project.parent.version}</version>
+			<scope>test</scope>
+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-rmi</artifactId>

+			<version>${project.parent.version}</version>

+			<scope>test</scope>

+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting-netty</artifactId>
+			<version>${project.parent.version}</version>
+			<scope>test</scope>
+		</dependency>

+		<dependency>

+			<groupId>org.springframework</groupId>

+			<artifactId>spring</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>javax.validation</groupId>

+			<artifactId>validation-api</artifactId>

+			<scope>test</scope>

+		</dependency>

+		<dependency>

+			<groupId>org.hibernate</groupId>

+			<artifactId>hibernate-validator</artifactId>

+			<scope>test</scope>

+		</dependency>
+	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>wagon-maven-plugin</artifactId>
+				<version>1.0-beta-3</version>
+				<executions>
+					<execution>
+						<id>upload-schema</id>
+						<phase>deploy</phase>
+						<goals>
+							<goal>upload-single</goal>
+						</goals>
+						<configuration>
+							<fromFile>${basedir}/src/main/resources/META-INF/dubbo.xsd</fromFile>
+							<url>dav:http://code.alibabatech.com/schema/dubbo/</url>
+							<serverId>opensesame.releases.account</serverId>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+</project>
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
new file mode 100644
index 0000000..851696d
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
@@ -0,0 +1,443 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.io.Serializable;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+

+/**

+ * AbstractConfig

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractConfig implements Serializable {

+

+    private static final long serialVersionUID = 4267533505537413570L;

+

+    protected static final Logger logger = LoggerFactory.getLogger(AbstractConfig.class);

+

+    private static final int MAX_LENGTH = 100;

+

+    private static final int MAX_PATH_LENGTH = 200;

+

+    private static final Pattern PATTERN_NAME = Pattern.compile("[\\-._0-9a-zA-Z]+");

+

+    private static final Pattern PATTERN_MULTI_NAME = Pattern.compile("[,\\-._0-9a-zA-Z]+");

+

+    private static final Pattern PATTERN_METHOD_NAME = Pattern.compile("[a-zA-Z][0-9a-zA-Z]*");

+    

+    private static final Pattern PATTERN_PATH = Pattern.compile("[/\\-$._0-9a-zA-Z]+");

+

+    private static final Pattern PATTERN_NAME_HAS_SYMBOL = Pattern.compile("[:*,/\\-._0-9a-zA-Z]+");

+

+    private static final Pattern PATTERN_KEY = Pattern.compile("[*,\\-._0-9a-zA-Z]+");

+

+    protected String id;

+

+    @Parameter(excluded = true)

+    public String getId() {

+        return id;

+    }

+

+    public void setId(String id) {

+        this.id = id;

+    }

+    

+    private static final Map<String, String> legacyProperties = new HashMap<String, String>();

+    static {

+        legacyProperties.put("dubbo.protocol.name", "dubbo.service.protocol");

+        legacyProperties.put("dubbo.protocol.host", "dubbo.service.server.host");

+        legacyProperties.put("dubbo.protocol.port", "dubbo.service.server.port");

+        legacyProperties.put("dubbo.protocol.threads", "dubbo.service.max.thread.pool.size");

+        legacyProperties.put("dubbo.consumer.timeout", "dubbo.service.invoke.timeout");

+        legacyProperties.put("dubbo.consumer.retries", "dubbo.service.max.retry.providers");

+        legacyProperties.put("dubbo.consumer.check", "dubbo.service.allow.no.provider");

+        legacyProperties.put("dubbo.service.url", "dubbo.service.address");

+    }

+    

+    private static String convertLegacyValue(String key, String value) {

+        if (value != null && value.length() > 0) {

+            if ("dubbo.service.max.retry.providers".equals(key)) {

+                return String.valueOf(Integer.parseInt(value) - 1);

+            } else if ("dubbo.service.allow.no.provider".equals(key)) {

+                return String.valueOf(! Boolean.parseBoolean(value));

+            }

+        }

+        return value;

+    }

+

+    protected static void appendProperties(AbstractConfig config) {

+        if (config == null) {

+            return;

+        }

+        String prefix = "dubbo." + getTagName(config.getClass()) + ".";

+        Method[] methods = config.getClass().getMethods();

+        for (Method method : methods) {

+            try {

+                String name = method.getName();

+                if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(method.getModifiers()) 

+                        && method.getParameterTypes().length == 1 && isPrimitive(method.getParameterTypes()[0])) {

+                    String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");

+                    String value = null;

+                    if (config.getId() != null && config.getId().length() > 0) {

+                        value = System.getProperty(prefix + config.getId() + "." + property);

+                    }

+                    if (value == null || value.length() == 0) {

+                        value = System.getProperty(prefix + property);

+                    }

+                    if (value == null || value.length() == 0) {

+                        Method getter;

+                        try {

+                            getter = config.getClass().getMethod("get" + name.substring(3), new Class<?>[0]);

+                        } catch (NoSuchMethodException e) {

+                            try {

+                                getter = config.getClass().getMethod("is" + name.substring(3), new Class<?>[0]);

+                            } catch (NoSuchMethodException e2) {

+                                getter = null;

+                            }

+                        }

+                        if (getter != null) {

+                            if (getter.invoke(config, new Object[0]) == null) {

+                                if (config.getId() != null && config.getId().length() > 0) {

+                                    value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);

+                                }

+                                if (value == null || value.length() == 0) {

+                                    value = ConfigUtils.getProperty(prefix + property);

+                                }

+                                if (value == null || value.length() == 0) {

+                                    String legacyKey = legacyProperties.get(prefix + property);

+                                    if (legacyKey != null && legacyKey.length() > 0) {

+                                        value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));

+                                    }

+                                }

+                                

+                            }

+                        }

+                    }

+                    if (value != null && value.length() > 0) {

+                        method.invoke(config, new Object[] {convertPrimitive(method.getParameterTypes()[0], value)});

+                    }

+                }

+            } catch (Exception e) {

+                logger.error(e.getMessage(), e);

+            }

+        }

+    }

+    

+    private static String getTagName(Class<?> cls) {

+        String tag = cls.getSimpleName();

+        for (String suffix : SUFFIXS) {

+            if (tag.endsWith(suffix)) {

+                tag = tag.substring(0, tag.length() - suffix.length());

+                break;

+            }

+        }

+        tag = tag.toLowerCase();

+        return tag;

+    }

+    

+    protected static void appendParameters(Map<String, String> parameters, Object config) {

+        appendParameters(parameters, config, null);

+    }

+    

+    @SuppressWarnings("unchecked")

+    protected static void appendParameters(Map<String, String> parameters, Object config, String prefix) {

+        if (config == null) {

+            return;

+        }

+        Method[] methods = config.getClass().getMethods();

+        for (Method method : methods) {

+            try {

+                String name = method.getName();

+                if ((name.startsWith("get") || name.startsWith("is")) 

+                        && ! "getClass".equals(name)

+                        && Modifier.isPublic(method.getModifiers()) 

+                        && method.getParameterTypes().length == 0

+                        && isPrimitive(method.getReturnType())) {

+                    Parameter parameter = method.getAnnotation(Parameter.class);

+                    if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {

+                        continue;

+                    }

+                    int i = name.startsWith("get") ? 3 : 2;

+                    String prop = StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), ".");

+                    String key;

+                    if (parameter != null && parameter.key() != null && parameter.key().length() > 0) {

+                        key = parameter.key();

+                    } else {

+                        key = prop;

+                    }

+                    Object value = method.invoke(config, new Object[0]);

+                    String str = String.valueOf(value).trim();

+                    if (value != null && str.length() > 0) {

+                        if (parameter != null && parameter.escaped()) {

+                            str = URL.encode(str);

+                        }

+                        if (parameter != null && parameter.append()) {

+                            String pre = (String)parameters.get(Constants.DEFAULT_KEY + "." + key);

+                            if (pre != null && pre.length() > 0) {

+                                str = pre + "," + str;

+                            }

+                            pre = (String)parameters.get(key);

+                            if (pre != null && pre.length() > 0) {

+                                str = pre + "," + str;

+                            }

+                        }

+                        if (prefix != null && prefix.length() > 0) {

+                            key = prefix + "." + key;

+                        }

+                        parameters.put(key, str);

+                    } else if (parameter != null && parameter.required()) {

+                        throw new IllegalStateException(config.getClass().getSimpleName() + "." + key + " == null");

+                    }

+                } else if ("getParameters".equals(name)

+                        && Modifier.isPublic(method.getModifiers()) 

+                        && method.getParameterTypes().length == 0

+                        && method.getReturnType() == Map.class) {

+                    Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);

+                    if (map != null && map.size() > 0) {

+                        String pre = (prefix != null && prefix.length() > 0 ? prefix + "." : "");

+                        for (Map.Entry<String, String> entry : map.entrySet()) {

+                            parameters.put(pre + entry.getKey().replace('-', '.'), entry.getValue());

+                        }

+                    }

+                }

+            } catch (Exception e) {

+                throw new IllegalStateException(e.getMessage(), e);

+            }

+        }

+    }

+    

+    protected static void appendAttributes(Map<Object, Object> parameters, Object config) {

+        appendAttributes(parameters, config, null);

+    }

+    

+    protected static void appendAttributes(Map<Object, Object> parameters, Object config, String prefix) {

+        if (config == null) {

+            return;

+        }

+        Method[] methods = config.getClass().getMethods();

+        for (Method method : methods) {

+            try {

+                String name = method.getName();

+                if ((name.startsWith("get") || name.startsWith("is")) 

+                        && ! "getClass".equals(name)

+                        && Modifier.isPublic(method.getModifiers()) 

+                        && method.getParameterTypes().length == 0

+                        && isPrimitive(method.getReturnType())) {

+                    Parameter parameter = method.getAnnotation(Parameter.class);

+                    if (parameter == null || !parameter.attribute())

+                        continue;

+                    String key;

+                    if (parameter != null && parameter.key() != null && parameter.key().length() > 0) {

+                        key = parameter.key();

+                    } else {

+                        int i = name.startsWith("get") ? 3 : 2;

+                        key = name.substring(i, i + 1).toLowerCase() + name.substring(i + 1);

+                    }

+                    Object value = method.invoke(config, new Object[0]);

+                    if (value != null) {

+                        if (prefix != null && prefix.length() > 0) {

+                            key = prefix + "." + key;

+                        }

+                        parameters.put(key, value);

+                    }

+                }

+            } catch (Exception e) {

+                throw new IllegalStateException(e.getMessage(), e);

+            }

+        }

+    }

+    

+    private static boolean isPrimitive(Class<?> type) {

+        return type.isPrimitive() 

+                || type == String.class 

+                || type == Character.class

+                || type == Boolean.class

+                || type == Byte.class

+                || type == Short.class

+                || type == Integer.class 

+                || type == Long.class

+                || type == Float.class 

+                || type == Double.class

+                || type == Object.class;

+    }

+    

+    private static Object convertPrimitive(Class<?> type, String value) {

+        if (type == char.class || type == Character.class) {

+            return value.length() > 0 ? value.charAt(0) : '\0';

+        } else if (type == boolean.class || type == Boolean.class) {

+            return Boolean.valueOf(value);

+        } else if (type == byte.class || type == Byte.class) {

+            return Byte.valueOf(value);

+        } else if (type == short.class || type == Short.class) {

+            return Short.valueOf(value);

+        } else if (type == int.class || type == Integer.class) {

+            return Integer.valueOf(value);

+        } else if (type == long.class || type == Long.class) {

+            return Long.valueOf(value);

+        } else if (type == float.class || type == Float.class) {

+            return Float.valueOf(value);

+        } else if (type == double.class || type == Double.class) {

+            return Double.valueOf(value);

+        }

+        return value;

+    }

+    

+    protected static void checkExtension(Class<?> type, String property, String value) {

+        checkName(property, value);

+        if (value != null && value.length() > 0 

+                && ! ExtensionLoader.getExtensionLoader(type).hasExtension(value)) {

+            throw new IllegalStateException("No such extension " + value + " for " + property + "/" + type.getName());

+        }

+    }

+    

+    protected static void checkMultiExtension(Class<?> type, String property, String value) {

+        checkMultiName(property, value);

+        if (value != null && value.length() > 0) {

+            String[] values = value.split("\\s*[,]+\\s*");

+            for (String v : values) {

+                if (v.startsWith(Constants.REMOVE_VALUE_PREFIX)) {

+                    v = v.substring(1);

+                }

+                if (! ExtensionLoader.getExtensionLoader(type).hasExtension(v)) {

+                    throw new IllegalStateException("No such extension " + v + " for " + property + "/" + type.getName());

+                }

+            }

+        }

+    }

+

+    protected static void checkLength(String property, String value) {

+        checkProperty(property, value, MAX_LENGTH, null);

+    }

+

+    protected static void checkPathLength(String property, String value) {

+        checkProperty(property, value, MAX_PATH_LENGTH, null);

+    }

+

+    protected static void checkName(String property, String value) {

+        checkProperty(property, value, MAX_LENGTH, PATTERN_NAME);

+    }

+    

+    protected static void checkNameHasSymbol(String property, String value) {

+        checkProperty(property, value, MAX_LENGTH, PATTERN_NAME_HAS_SYMBOL);

+    }

+

+    protected static void checkKey(String property, String value) {

+        checkProperty(property, value, MAX_LENGTH, PATTERN_KEY);

+    }

+    

+    protected static void checkMultiName(String property, String value) {

+        checkProperty(property, value, MAX_LENGTH, PATTERN_MULTI_NAME);

+    }

+

+    protected static void checkPathName(String property, String value) {

+        checkProperty(property, value, MAX_PATH_LENGTH, PATTERN_PATH);

+    }

+

+    protected static void checkMethodName(String property, String value) {

+        checkProperty(property, value, MAX_LENGTH, PATTERN_METHOD_NAME);

+    }

+    

+    protected static void checkParameterName(Map<String, String> parameters) {

+        if (parameters == null || parameters.size() == 0) {

+            return;

+        }

+        for (Map.Entry<String, String> entry : parameters.entrySet()) {

+            //change by tony.chenl parameter value maybe has colon.for example napoli address

+            checkNameHasSymbol(entry.getKey(), entry.getValue());

+        }

+    }

+    

+    protected static void checkProperty(String property, String value, int maxlength, Pattern pattern) {

+        if (value == null || value.length() == 0) {

+            return;

+        }

+        if(value.length() > maxlength){

+            throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" is longer than " + maxlength);

+        }

+        if (pattern != null) {

+            Matcher matcher = pattern.matcher(value);

+            if(! matcher.matches()) {

+                throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" contain illegal charactor, only digit, letter, '-', '_' and '.' is legal.");

+            }

+        }

+    }

+    

+    static {

+        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

+            public void run() {

+                if (logger.isInfoEnabled()) {

+                    logger.info("Run shutdown hook now.");

+                }

+                ProtocolConfig.destroyAll();

+            }

+        }, "DubboShutdownHook"));

+    }

+    

+    private static final String[] SUFFIXS = new String[] {"Config", "Bean"};

+    

+    @Override

+    public String toString() {

+        try {

+            StringBuilder buf = new StringBuilder();

+            buf.append("<dubbo:");

+            buf.append(getTagName(getClass()));

+            Method[] methods = getClass().getMethods();

+            for (Method method : methods) {

+                try {

+                    String name = method.getName();

+                    if ((name.startsWith("get") || name.startsWith("is")) 

+                            && ! "getClass".equals(name) && ! "get".equals(name) && ! "is".equals(name)

+                            && Modifier.isPublic(method.getModifiers()) 

+                            && method.getParameterTypes().length == 0

+                            && isPrimitive(method.getReturnType())) {

+                        int i = name.startsWith("get") ? 3 : 2;

+                        String key = name.substring(i, i + 1).toLowerCase() + name.substring(i + 1);

+                        Object value = method.invoke(this, new Object[0]);

+                        if (value != null) {

+                            buf.append(" ");

+                            buf.append(key);

+                            buf.append("=\"");

+                            buf.append(value);

+                            buf.append("\"");

+                        }

+                    }

+                } catch (Exception e) {

+                    logger.warn(e.getMessage(), e);

+                }

+            }

+            buf.append(" />");

+            return buf.toString();

+        } catch (Throwable t) { // 防御性容错

+            logger.warn(t.getMessage(), t);

+            return super.toString();

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConsumerConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConsumerConfig.java
new file mode 100644
index 0000000..9d37065
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConsumerConfig.java
@@ -0,0 +1,153 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.rpc.InvokerListener;

+

+

+/**

+ * AbstractConsumerConfig

+ * 

+ * @see com.alibaba.dubbo.config.ReferenceConfig

+ * @author william.liangf

+ */

+public abstract class AbstractConsumerConfig extends AbstractReferenceConfig {

+

+    private static final long serialVersionUID = -2786526984373031126L;

+

+    // ======== 引用缺省值,当引用属性未设置时使用该缺省值替代  ========

+    

+    // 检查服务提供者是否存在

+    protected Boolean             check;

+

+    // 是否加载时即刻初始化

+    protected Boolean             init;

+

+    // 是否使用泛接口

+    protected Boolean             generic;

+

+    // 优先从JVM内获取引用实例

+    protected Boolean             injvm;

+    

+    // lazy create connection

+    protected Boolean             lazy;

+

+    protected String              reconnect;

+    

+    protected Boolean             sticky;

+    

+    //stub是否支持event事件. //TODO slove merge problem 

+    protected Boolean             stubevent ;//= Constants.DEFAULT_STUB_EVENT;

+    

+    public Boolean isCheck() {

+        return check;

+    }

+

+    public void setCheck(Boolean check) {

+        this.check = check;

+    }

+

+    public Boolean isInit() {

+        return init;

+    }

+

+    public void setInit(Boolean init) {

+        this.init = init;

+    }

+

+    @Parameter(excluded = true)

+    public Boolean isGeneric() {

+        return generic;

+    }

+

+    public void setGeneric(Boolean generic) {

+        this.generic = generic;

+    }

+

+    public Boolean isInjvm() {

+        return injvm;

+    }

+    

+    public void setInjvm(Boolean injvm) {

+        this.injvm = injvm;

+    }

+

+    @Parameter(key = Constants.REFERENCE_FILTER_KEY, append = true)

+    public String getFilter() {

+        return super.getFilter();

+    }

+

+    @Parameter(key = Constants.INVOKER_LISTENER_KEY, append = true)

+    public String getListener() {

+        return super.getListener();

+    }

+

+    @Override

+    public void setListener(String listener) {

+        checkMultiExtension(InvokerListener.class, "listener", listener);

+        super.setListener(listener);

+    }

+

+    @Parameter(key = Constants.LAZY_CONNECT_KEY)

+    public Boolean getLazy() {

+        return lazy;

+    }

+

+    public void setLazy(Boolean lazy) {

+        this.lazy = lazy;

+    }

+

+    @Override

+    public void setOnconnect(String onconnect) {

+        if (onconnect != null && onconnect.length() >0){

+            this.stubevent = true;

+        }

+        super.setOnconnect(onconnect);

+    }

+

+    @Override

+    public void setOndisconnect(String ondisconnect) {

+        if (ondisconnect != null && ondisconnect.length() >0){

+            this.stubevent = true;

+        }

+        super.setOndisconnect(ondisconnect);

+    }

+

+    @Parameter(key = Constants.STUB_EVENT_KEY)

+    public Boolean getStubevent() {

+        return stubevent;

+    }

+    

+    @Parameter(key = Constants.RECONNECT_KEY)

+    public String getReconnect() {

+        return reconnect;

+    }

+

+    public void setReconnect(String reconnect) {

+        this.reconnect = reconnect;

+    }

+

+    @Parameter(key = Constants.CLUSTER_STICKY_KEY)

+    public Boolean getSticky() {

+        return sticky;

+    }

+

+    public void setSticky(Boolean sticky) {

+        this.sticky = sticky;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java
new file mode 100644
index 0000000..592c4ac
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java
@@ -0,0 +1,158 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+

+/**

+ * AbstractMethodConfig

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractMethodConfig extends AbstractConfig {

+

+    private static final long serialVersionUID = 1L;

+

+    // 远程调用超时时间(毫秒)

+    protected Integer             timeout;

+

+    // 重试次数

+    protected Integer             retries;

+

+    // 最大并发调用

+    protected Integer             actives;

+    

+    // 负载均衡

+    protected String              loadbalance;

+

+    // 是否异步

+    protected Boolean             async;

+    

+    // 异步发送是否等待发送成功

+    protected Boolean             sent;

+

+    // 服务接口的失败mock实现类名

+    protected String              mock;

+

+    // 服务接口的失败mock实现类名

+    protected String              cache;

+

+    // 服务接口的失败mock实现类名

+    protected String              validation;

+

+    // 自定义参数

+    protected Map<String, String> parameters;

+

+    public Integer getTimeout() {

+        return timeout;

+    }

+

+    public void setTimeout(Integer timeout) {

+        this.timeout = timeout;

+    }

+

+    public Integer getRetries() {

+        return retries;

+    }

+

+    public void setRetries(Integer retries) {

+        this.retries = retries;

+    }

+

+    public String getLoadbalance() {

+        return loadbalance;

+    }

+

+    public void setLoadbalance(String loadbalance) {

+        checkExtension(LoadBalance.class, "loadbalance", loadbalance);

+        this.loadbalance = loadbalance;

+    }

+

+    public Boolean isAsync() {

+        return async;

+    }

+

+    public void setAsync(Boolean async) {

+        this.async = async;

+    }

+

+    public Integer getActives() {

+        return actives;

+    }

+    

+    public void setActives(Integer actives) {

+        this.actives = actives;

+    }

+

+    public Boolean getSent() {

+        return sent;

+    }

+

+    public void setSent(Boolean sent) {

+        this.sent = sent;

+    }

+

+    @Parameter(escaped = true)

+    public String getMock() {

+        return mock;

+    }

+

+    public void setMock(String mock) {

+        if (mock != null && mock.startsWith(Constants.RETURN_PREFIX)) {

+            checkLength("mock", mock);

+        } else {

+            checkName("mock", mock);

+        }

+        this.mock = mock;

+    }

+    

+    public void setMock(Boolean mock) {

+        if (mock == null) {

+            setMock((String) null);

+        } else {

+            setMock(String.valueOf(mock));

+        }

+    }

+

+    public String getCache() {

+        return cache;

+    }

+

+    public void setCache(String cache) {

+        this.cache = cache;

+    }

+

+    public String getValidation() {

+        return validation;

+    }

+

+    public void setValidation(String validation) {

+        this.validation = validation;

+    }

+

+    public Map<String, String> getParameters() {

+        return parameters;

+    }

+

+    public void setParameters(Map<String, String> parameters) {

+        checkParameterName(parameters);

+        this.parameters = parameters;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.java
new file mode 100644
index 0000000..648f337
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.java
@@ -0,0 +1,473 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.common.utils.UrlUtils;

+import com.alibaba.dubbo.monitor.MonitorFactory;

+import com.alibaba.dubbo.monitor.MonitorService;

+import com.alibaba.dubbo.registry.RegistryFactory;

+import com.alibaba.dubbo.registry.RegistryService;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.InvokerListener;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.support.MockInvoker;

+

+/**

+ * AbstractDefaultConfig

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractReferenceConfig extends AbstractMethodConfig {

+

+    private static final long      serialVersionUID = -1559314110797223229L;

+

+    // 服务接口的本地实现类名

+    protected String               local;

+

+    // 服务接口的本地实现类名

+    protected String               stub;

+

+    // 服务监控

+    protected MonitorConfig        monitor;

+    

+    // 代理类型

+    protected String               proxy;

+    

+    // 集群方式

+    protected String               cluster;

+

+    // 过滤器

+    protected String               filter;

+    

+    // 监听器

+    protected String               listener;

+

+    // 负责人

+    protected String               owner;

+

+    // 连接数限制,0表示共享连接,否则为该服务独享连接数

+    protected Integer              connections;

+    

+    // 连接数限制

+    protected String               layer;

+    

+    // 应用信息

+    protected ApplicationConfig    application;

+

+    // 注册中心

+    protected List<RegistryConfig> registries;

+    

+    // callback实例个数限制

+    private Integer                callbacks;

+    

+    // 连接事件

+    protected String              onconnect;

+    

+    // 断开事件

+    protected String              ondisconnect;

+

+    protected void checkRegistry() {

+        // 兼容旧版本

+        if (registries == null || registries.size() == 0) {

+            String address = ConfigUtils.getProperty("dubbo.registry.address");

+            if (address != null && address.length() > 0) {

+                registries = new ArrayList<RegistryConfig>();

+                String[] as = address.split("\\s*[|]+\\s*");

+                for (String a : as) {

+                    RegistryConfig registryConfig = new RegistryConfig();

+                    registryConfig.setAddress(a);

+                    registries.add(registryConfig);

+                }

+            }

+        }

+        if (registries == null || registries.size() == 0) {

+            throw new IllegalStateException((getClass().getSimpleName().startsWith("Reference") 

+                    ? "No such any registry to refer service in consumer " 

+                        : "No such any registry to export service in provider ")

+                                                    + NetUtils.getLocalHost()

+                                                    + " use dubbo version "

+                                                    + Version.getVersion()

+                                                    + ", Please add <dubbo:registry address=\"...\" /> to your spring config. If you want unregister, please set <dubbo:service registry=\"N/A\" />");

+        }

+        for (RegistryConfig registryConfig : registries) {

+            appendProperties(registryConfig);

+        }

+    }

+

+    @SuppressWarnings("deprecation")

+    protected void checkApplication() {

+        // 兼容旧版本

+        if (application == null) {

+            String applicationName = ConfigUtils.getProperty("dubbo.application.name");

+            if (applicationName != null && applicationName.length() > 0) {

+                application = new ApplicationConfig();

+            }

+        }

+        if (application == null) {

+            throw new IllegalStateException(

+                                            "No such application config! Please add <dubbo:application name=\"...\" /> to your spring config.");

+        }

+        appendProperties(application);

+        

+        String wait = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_KEY);

+        if (wait != null && wait.trim().length() > 0) {

+            System.setProperty(Constants.SHUTDOWN_WAIT_KEY, wait.trim());

+        } else {

+            wait = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_SECONDS_KEY);

+            if (wait != null && wait.trim().length() > 0) {

+                System.setProperty(Constants.SHUTDOWN_WAIT_SECONDS_KEY, wait.trim());

+            }

+        }

+    }

+    

+    protected List<URL> loadRegistries() {

+        checkRegistry();

+        List<URL> registryList = new ArrayList<URL>();

+        if (registries != null && registries.size() > 0) {

+            for (RegistryConfig config : registries) {

+                String address = config.getAddress();

+                if (address == null || address.length() == 0) {

+                    throw new IllegalStateException("registry address == null");

+                }

+                String sysaddress = System.getProperty("dubbo.registry.address");

+                if (sysaddress != null && sysaddress.length() > 0) {

+                    address = sysaddress;

+                }

+                if (address != null && address.length() > 0 

+                        && ! RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {

+                    Map<String, String> map = new HashMap<String, String>();

+                    appendParameters(map, application);

+                    appendParameters(map, config);

+                    map.put("path", RegistryService.class.getName());

+                    if (! map.containsKey("protocol")) {

+                        if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) {

+                            map.put("protocol", "remote");

+                        } else {

+                            map.put("protocol", "dubbo");

+                        }

+                    }

+                    List<URL> urls = UrlUtils.parseURLs(address, map);

+                    for (URL url : urls) {

+                        url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());

+                        url = url.setProtocol(Constants.REGISTRY_PROTOCOL);

+                        registryList.add(url);

+                    }

+                }

+            }

+        }

+        return registryList;

+    }

+    

+    protected URL loadMonitor(URL registryURL) {

+        if (monitor == null) {

+            String monitorAddress = ConfigUtils.getProperty("dubbo.monitor.address");

+            String monitorProtocol = ConfigUtils.getProperty("dubbo.monitor.protocol");

+            if (monitorAddress != null && monitorAddress.length() > 0

+                    || monitorProtocol != null && monitorProtocol.length() > 0) {

+                monitor = new MonitorConfig();

+            } else {

+                return null;

+            }

+        }

+        appendProperties(monitor);

+        Map<String, String> map = new HashMap<String, String>();

+        map.put(Constants.INTERFACE_KEY, MonitorService.class.getName());

+        appendParameters(map, monitor);

+        String address = monitor.getAddress();

+        String sysaddress = System.getProperty("dubbo.monitor.address");

+        if (sysaddress != null && sysaddress.length() > 0) {

+            address = sysaddress;

+        }

+        if (ConfigUtils.isNotEmpty(address)) {

+            if (! map.containsKey(Constants.PROTOCOL_KEY)) {

+                if (ExtensionLoader.getExtensionLoader(MonitorFactory.class).hasExtension("logstat")) {

+                    map.put(Constants.PROTOCOL_KEY, "logstat");

+                } else {

+                    map.put(Constants.PROTOCOL_KEY, "dubbo");

+                }

+            }

+            return UrlUtils.parseURL(address, map);

+        } else if (Constants.REGISTRY_PROTOCOL.equals(monitor.getProtocol()) && registryURL != null) {

+            return registryURL.setProtocol("dubbo").addParameter(Constants.PROTOCOL_KEY, "registry").addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map));

+        }

+        return null;

+    }

+    

+    protected void checkInterfaceAndMethods(Class<?> interfaceClass, List<MethodConfig> methods) {

+        // 接口不能为空

+        if (interfaceClass == null) {

+            throw new IllegalStateException("interface not allow null!");

+        }

+        // 检查接口类型必需为接口

+        if(! interfaceClass.isInterface()) { 

+            throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");

+        }

+        // 检查方法是否在接口中存在

+        if (methods != null && methods.size() > 0) {

+            for (MethodConfig methodBean : methods) {

+                String methodName = methodBean.getName();

+                if (methodName == null || methodName.length() == 0) {

+                    throw new IllegalStateException("<dubbo:method> name attribute is required! Please check: <dubbo:service interface=\"" + interfaceClass.getName() + "\" ... ><dubbo:method name=\"\" ... /></<dubbo:reference>");

+                }

+                boolean hasMethod = false;

+                for (java.lang.reflect.Method method : interfaceClass.getMethods()) {

+                    if (method.getName().equals(methodName)) {

+                        hasMethod = true;

+                        break;

+                    }

+                }

+                if (!hasMethod) {

+                    throw new IllegalStateException("The interface " + interfaceClass.getName()

+                            + " not found method " + methodName);

+                }

+            }

+        }

+    }

+    

+    protected void checkStubAndMock(Class<?> interfaceClass) {

+        if (ConfigUtils.isNotEmpty(local)) {

+            Class<?> localClass = ConfigUtils.isDefault(local) ? ReflectUtils.forName(interfaceClass.getName() + "Local") : ReflectUtils.forName(local);

+            if (! interfaceClass.isAssignableFrom(localClass)) {

+                throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceClass.getName());

+            }

+            try {

+                ReflectUtils.findConstructor(localClass, interfaceClass);

+            } catch (NoSuchMethodException e) {

+                throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() + "(" + interfaceClass.getName() + ")\" in local implemention class " + localClass.getName());

+            }

+        }

+        if (ConfigUtils.isNotEmpty(stub)) {

+            Class<?> localClass = ConfigUtils.isDefault(stub) ? ReflectUtils.forName(interfaceClass.getName() + "Stub") : ReflectUtils.forName(stub);

+            if (! interfaceClass.isAssignableFrom(localClass)) {

+                throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceClass.getName());

+            }

+            try {

+                ReflectUtils.findConstructor(localClass, interfaceClass);

+            } catch (NoSuchMethodException e) {

+                throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() + "(" + interfaceClass.getName() + ")\" in local implemention class " + localClass.getName());

+            }

+        }

+        if (ConfigUtils.isNotEmpty(mock)) {

+            if (mock.startsWith(Constants.RETURN_PREFIX)) {

+                String value = mock.substring(Constants.RETURN_PREFIX.length());

+                try {

+                    MockInvoker.parseMockValue(value);

+                } catch (Exception e) {

+                    throw new IllegalStateException("Illegal mock json value in <dubbo:service ... mock=\"" + mock + "\" />");

+                }

+            } else {

+                Class<?> mockClass = ConfigUtils.isDefault(mock) ? ReflectUtils.forName(interfaceClass.getName() + "Mock") : ReflectUtils.forName(mock);

+                if (! interfaceClass.isAssignableFrom(mockClass)) {

+                    throw new IllegalStateException("The mock implemention class " + mockClass.getName() + " not implement interface " + interfaceClass.getName());

+                }

+                try {

+                    mockClass.getConstructor(new Class<?>[0]);

+                } catch (NoSuchMethodException e) {

+                    throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName());

+                }

+            }

+        }

+    }

+

+    /**

+     * @deprecated Replace to <code>getStub()</code>

+     * @return local

+     */

+    @Deprecated

+    public String getLocal() {

+        return local;

+    }

+

+    /**

+     * @deprecated Replace to <code>setStub(String)</code>

+     * @param local

+     */

+    @Deprecated

+    public void setLocal(String local) {

+        checkName("local", local);

+        this.local = local;

+    }

+

+    /**

+     * @deprecated Replace to <code>setStub(Boolean)</code>

+     * @param local

+     */

+    @Deprecated

+    public void setLocal(Boolean local) {

+        if (local == null) {

+            setLocal((String) null);

+        } else {

+            setLocal(String.valueOf(local));

+        }

+    }

+    

+    public String getStub() {

+        return stub;

+    }

+

+    public void setStub(String stub) {

+        checkName("stub", stub);

+        this.stub = stub;

+    }

+

+    public void setStub(Boolean stub) {

+        if (local == null) {

+            setStub((String) null);

+        } else {

+            setStub(String.valueOf(stub));

+        }

+    }

+    

+    public String getCluster() {

+        return cluster;

+    }

+

+    public void setCluster(String cluster) {

+        checkExtension(Cluster.class, "cluster", cluster);

+        this.cluster = cluster;

+    }

+

+    public String getProxy() {

+        return proxy;

+    }

+    

+    public void setProxy(String proxy) {

+        checkExtension(ProxyFactory.class, "proxy", proxy);

+        this.proxy = proxy;

+    }

+

+    public Integer getConnections() {

+        return connections;

+    }

+

+    public void setConnections(Integer connections) {

+        this.connections = connections;

+    }

+

+    @Parameter(key = Constants.REFERENCE_FILTER_KEY, append = true)

+    public String getFilter() {

+        return filter;

+    }

+    

+    public void setFilter(String filter) {

+        checkMultiExtension(Filter.class, "filter", filter);

+        this.filter = filter;

+    }

+

+    @Parameter(key = Constants.INVOKER_LISTENER_KEY, append = true)

+    public String getListener() {

+        checkMultiExtension(InvokerListener.class, "listener", listener);

+        return listener;

+    }

+    

+    public void setListener(String listener) {

+        this.listener = listener;

+    }

+

+    public String getLayer() {

+        return layer;

+    }

+

+    public void setLayer(String layer) {

+        checkNameHasSymbol("layer", layer);

+        this.layer = layer;

+    }

+

+    public ApplicationConfig getApplication() {

+        return application;

+    }

+

+    public void setApplication(ApplicationConfig application) {

+        this.application = application;

+    }

+

+    public RegistryConfig getRegistry() {

+        return registries == null || registries.size() == 0 ? null : registries.get(0);

+    }

+

+    public void setRegistry(RegistryConfig registry) {

+        List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);

+        registries.add(registry);

+        this.registries = registries;

+    }

+

+    public List<RegistryConfig> getRegistries() {

+        return registries;

+    }

+

+    @SuppressWarnings({ "unchecked" })

+    public void setRegistries(List<? extends RegistryConfig> registries) {

+        this.registries = (List<RegistryConfig>)registries;

+    }

+

+    public MonitorConfig getMonitor() {

+        return monitor;

+    }

+

+    public void setMonitor(MonitorConfig monitor) {

+        this.monitor = monitor;

+    }

+

+    public void setMonitor(String monitor) {

+        this.monitor = new MonitorConfig(monitor);

+    }

+

+    public String getOwner() {

+        return owner;

+    }

+

+    public void setOwner(String owner) {

+        this.owner = owner;

+    }

+

+    public void setCallbacks(Integer callbacks) {

+        this.callbacks = callbacks;

+    }

+

+    public Integer getCallbacks() {

+        return callbacks;

+    }

+

+    public String getOnconnect() {

+        return onconnect;

+    }

+

+    public void setOnconnect(String onconnect) {

+        this.onconnect = onconnect;

+    }

+

+    public String getOndisconnect() {

+        return ondisconnect;

+    }

+

+    public void setOndisconnect(String ondisconnect) {

+        this.ondisconnect = ondisconnect;

+    }

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java
new file mode 100644
index 0000000..06b963f
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java
@@ -0,0 +1,197 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.Arrays;

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.rpc.ExporterListener;

+

+/**

+ * AbstractServiceConfig

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractServiceConfig extends AbstractReferenceConfig {

+

+    private static final long serialVersionUID = 1L;

+    

+    // 服务版本

+    protected String              version;

+

+    // 服务分组

+    protected String              group;

+    

+    // 服务是否已经deprecated

+    protected Boolean             deprecated;

+    

+    // 延迟注册

+    protected Integer             delay;

+

+    // 权重

+    protected Integer             weight;

+

+    // 应用文档

+    protected String              document;

+    

+    // 在注册中心上注册成动态的还是静态的服务

+    protected Boolean             dynamic;

+

+    // 是否使用令牌

+    protected String              token;

+    

+    // 访问日志

+    protected String              accesslog;

+    

+    // 允许执行请求数

+    private Integer               executes;

+

+    protected List<ProtocolConfig> protocols;

+

+    public String getVersion() {

+        return version;

+    }

+

+    public void setVersion(String version) {

+        checkName("version", version);

+        this.version = version;

+    }

+

+    public String getGroup() {

+        return group;

+    }

+

+    public void setGroup(String group) {

+        checkKey("group", group);

+        this.group = group;

+    }

+

+	public Integer getDelay() {

+		return delay;

+	}

+

+	public void setDelay(Integer delay) {

+	    this.delay = delay;

+	}

+    

+    public Integer getWeight() {

+        return weight;

+    }

+    

+    public void setWeight(Integer weight) {

+        this.weight = weight;

+    }

+    

+    @Parameter(escaped = true)

+    public String getDocument() {

+        return document;

+    }

+    

+    public void setDocument(String document) {

+        this.document = document;

+    }

+

+	public String getToken() {

+		return token;

+	}

+

+	public void setToken(String token) {

+	    checkName("token", token);

+		this.token = token;

+	}

+	

+	public void setToken(Boolean token) {

+        if (token == null) {

+            setToken((String) null);

+        } else {

+            setToken(String.valueOf(token));

+        }

+    }

+

+    public Boolean isDeprecated() {

+        return deprecated;

+    }

+

+    public void setDeprecated(Boolean deprecated) {

+        this.deprecated = deprecated;

+    }

+

+    public Boolean isDynamic() {

+        return dynamic;

+    }

+

+    public void setDynamic(Boolean dynamic) {

+        this.dynamic = dynamic;

+    }

+

+    public List<ProtocolConfig> getProtocols() {

+        return protocols;

+    }

+

+    @SuppressWarnings({ "unchecked" })

+    public void setProtocols(List<? extends ProtocolConfig> protocols) {

+        this.protocols = (List<ProtocolConfig>)protocols;

+    }

+

+    public ProtocolConfig getProtocol() {

+        return protocols == null || protocols.size() == 0 ? null : protocols.get(0);

+    }

+

+    public void setProtocol(ProtocolConfig protocol) {

+        this.protocols = Arrays.asList(new ProtocolConfig[] {protocol});

+    }

+

+    public String getAccesslog() {

+        return accesslog;

+    }

+

+    public void setAccesslog(String accesslog) {

+        this.accesslog = accesslog;

+    }

+    

+    public void setAccesslog(Boolean accesslog) {

+        if (accesslog == null) {

+            setAccesslog((String) null);

+        } else {

+            setAccesslog(String.valueOf(accesslog));

+        }

+    }

+

+    public Integer getExecutes() {

+        return executes;

+    }

+    

+    public void setExecutes(Integer executes) {

+        this.executes = executes;

+    }

+

+    @Parameter(key = Constants.SERVICE_FILTER_KEY, append = true)

+    public String getFilter() {

+        return super.getFilter();

+    }

+

+    @Parameter(key = Constants.EXPORTER_LISTENER_KEY, append = true)

+    public String getListener() {

+        return super.getListener();

+    }

+

+    @Override

+    public void setListener(String listener) {

+        checkMultiExtension(ExporterListener.class, "listener", listener);

+        super.setListener(listener);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.java
new file mode 100644
index 0000000..04b366e
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.java
@@ -0,0 +1,159 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler;

+

+

+/**

+ * ApplicationConfig

+ * 

+ * @author william.liangf

+ */

+public class ApplicationConfig extends AbstractConfig {

+

+	private static final long serialVersionUID = 5508512956753757169L;

+	

+    // 应用名称

+    private String            name;

+    

+    // 应用负责人

+    private String            owner;

+

+    // 组织名(BU或部门)

+    private String            organization;

+    

+    // 分层

+    private String            architecture;

+    

+    // 环境,如:dev/test/run

+    private String            environment;

+

+    // Java代码编译器

+    private String            compiler;

+    

+    // 注册中心

+    protected List<RegistryConfig> registries;

+    

+    // 服务监控

+    private MonitorConfig     monitor;

+    

+    public ApplicationConfig() {

+    }

+    

+    public ApplicationConfig(String name) {

+        setName(name);

+    }

+    

+    @Parameter(key = Constants.APPLICATION_KEY, required = true)

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        checkName("name", name);

+        this.name = name;

+        if (id == null || id.length() == 0) {

+            id = name;

+        }

+    }

+

+    public String getOwner() {

+        return owner;

+    }

+

+    public void setOwner(String owner) {

+        checkName("owner", owner);

+        this.owner = owner;

+    }

+

+	public String getOrganization() {

+		return organization;

+	}

+

+	public void setOrganization(String organization) {

+	    checkName("organization", organization);

+		this.organization = organization;

+	}

+

+	public String getArchitecture() {

+        return architecture;

+    }

+

+    public void setArchitecture(String architecture) {

+        checkName("architecture", architecture);

+        this.architecture = architecture;

+    }

+

+    public String getEnvironment() {

+		return environment;

+	}

+

+	public void setEnvironment(String environment) {

+	    checkName("environment", environment);

+	    if(environment != null) {

+            if (! ("develop".equals(environment) || "test".equals(environment) || "product".equals(environment))) {

+                throw new IllegalStateException("Unsupported environment: " + environment + ", only support develop/test/product, default is product.");

+            }

+        }

+		this.environment = environment;

+	}

+

+    public RegistryConfig getRegistry() {

+        return registries == null || registries.size() == 0 ? null : registries.get(0);

+    }

+

+    public void setRegistry(RegistryConfig registry) {

+        List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);

+        registries.add(registry);

+        this.registries = registries;

+    }

+

+    public List<RegistryConfig> getRegistries() {

+        return registries;

+    }

+

+    @SuppressWarnings({ "unchecked" })

+    public void setRegistries(List<? extends RegistryConfig> registries) {

+        this.registries = (List<RegistryConfig>)registries;

+    }

+

+    public MonitorConfig getMonitor() {

+        return monitor;

+    }

+

+    public void setMonitor(MonitorConfig monitor) {

+        this.monitor = monitor;

+    }

+

+    public void setMonitor(String monitor) {

+        this.monitor = new MonitorConfig(monitor);

+    }

+

+    public String getCompiler() {

+        return compiler;

+    }

+

+    public void setCompiler(String compiler) {

+        this.compiler = compiler;

+        AdaptiveCompiler.setDefaultCompiler(compiler);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.java
new file mode 100644
index 0000000..6648e12
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;
+
+import java.io.Serializable;
+
+/**
+ * @author chao.liuc
+ */
+public class ArgumentConfig implements Serializable {
+
+    private static final long serialVersionUID = -2165482463925213595L;
+
+    //arugment index -1 represents not set
+    private Integer index = -1;
+
+    //argument type
+    private String  type;
+    
+    //callback interface
+    private Boolean callback;
+
+    public void setIndex(Integer index) {
+        this.index = index;
+    }
+    @Parameter(excluded = true)
+    public Integer getIndex() {
+        return index;
+    }
+    @Parameter(excluded = true)
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public void setCallback(Boolean callback) {
+        this.callback = callback;
+    }
+
+    public Boolean isCallback() {
+        return callback;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.java
new file mode 100644
index 0000000..0b510aa
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+/**

+ * ConsumerConfig

+ * 

+ * @author william.liangf

+ */

+public class ConsumerConfig extends AbstractConsumerConfig {

+

+    private static final long serialVersionUID = 2827274711143680600L;

+

+    @Override

+    public void setTimeout(Integer timeout) {

+        super.setTimeout(timeout);

+        String rmiTimeout = System.getProperty("sun.rmi.transport.tcp.responseTimeout");

+        if (timeout != null && timeout > 0

+                && (rmiTimeout == null || rmiTimeout.length() == 0)) {

+            System.setProperty("sun.rmi.transport.tcp.responseTimeout", String.valueOf(timeout));

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/MethodConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/MethodConfig.java
new file mode 100644
index 0000000..4efc6af
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/MethodConfig.java
@@ -0,0 +1,222 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+

+/**

+ * MethodConfig

+ * 

+ * @author william.liangf

+ */

+public class MethodConfig extends AbstractMethodConfig {

+

+    private static final long serialVersionUID = 884908855422675941L;

+

+    // 方法名

+    private String            name;

+    

+    // 统计参数

+    private Integer           stat;

+

+    // 是否重试

+    private Boolean           retry;

+

+    // 是否为可靠异步

+    private Boolean           reliable;

+

+    // 方法使用线程数限制

+    private Integer           executes;

+    

+    // 是否过时

+    private Boolean           deprecated;

+

+    // 合并器

+    private String            merger;

+    

+    // 是否需要开启stiky策略

+    private Boolean           sticky;

+

+    // 是否需要返回

+    private Boolean           isReturn;

+    

+    //异步调用回调实例

+    private Object            oninvoke;

+

+    //异步调用回调方法

+    private String            oninvokeMethod;

+    

+    //异步调用回调实例

+    private Object            onreturn;

+

+    //异步调用回调方法

+    private String            onreturnMethod;

+    

+    //异步调用异常回调实例

+    private Object            onthrow;

+    

+    //异步调用异常回调方法

+    private String            onthrowMethod;

+    

+    private List<ArgumentConfig> arguments;

+    

+    @Parameter(excluded = true)

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        checkMethodName("name", name);

+        this.name = name;

+        if (id == null || id.length() == 0) {

+            id = name;

+        }

+    }

+    

+    public Integer getStat() {

+        return stat;

+    }

+    

+    @Deprecated

+    public void setStat(Integer stat) {

+        this.stat = stat;

+    }

+

+    @Deprecated

+    public Boolean isRetry() {

+        return retry;

+    }

+

+    @Deprecated

+    public void setRetry(Boolean retry) {

+        this.retry = retry;

+    }

+

+    @Deprecated

+    public Boolean isReliable() {

+        return reliable;

+    }

+

+    @Deprecated

+    public void setReliable(Boolean reliable) {

+        this.reliable = reliable;

+    }

+

+    public Integer getExecutes() {

+        return executes;

+    }

+

+    public void setExecutes(Integer executes) {

+        this.executes = executes;

+    }

+

+    public Boolean getDeprecated() {

+        return deprecated;

+    }

+

+    public void setDeprecated(Boolean deprecated) {

+        this.deprecated = deprecated;

+    }

+

+    @SuppressWarnings("unchecked")

+    public void setArguments(List<? extends ArgumentConfig> arguments) {

+        this.arguments = (List<ArgumentConfig>) arguments;

+    }

+

+    public List<ArgumentConfig> getArguments() {

+        return arguments;

+    }

+    

+    public Boolean getSticky() {

+        return sticky;

+    }

+

+    public void setSticky(Boolean sticky) {

+        this.sticky = sticky;

+    }

+

+    @Parameter(key = Constants.ON_RETURN_INSTANCE_KEY, excluded = true, attribute = true)

+    public Object getOnreturn() {

+        return onreturn;

+    }

+    

+    public void setOnreturn(Object onreturn) {

+        this.onreturn = onreturn;

+    }

+    

+    @Parameter(key = Constants.ON_RETURN_METHOD_KEY, excluded = true, attribute = true)

+    public String getOnreturnMethod() {

+        return onreturnMethod;

+    }

+

+    public void setOnreturnMethod(String onreturnMethod) {

+        this.onreturnMethod = onreturnMethod;

+    }

+

+    @Parameter(key = Constants.ON_THROW_INSTANCE_KEY, excluded = true, attribute = true)

+    public Object getOnthrow() {

+        return onthrow;

+    }

+

+    public void setOnthrow(Object onthrow) {

+        this.onthrow = onthrow;

+    }

+    

+    @Parameter(key = Constants.ON_THROW_METHOD_KEY, excluded = true, attribute = true)

+    public String getOnthrowMethod() {

+        return onthrowMethod;

+    }

+

+    public void setOnthrowMethod(String onthrowMethod) {

+        this.onthrowMethod = onthrowMethod;

+    }

+    

+    @Parameter(key = Constants.ON_INVOKE_INSTANCE_KEY, excluded = true, attribute = true)

+    public Object getOninvoke() {

+        return oninvoke;

+    }

+    

+    public void setOninvoke(Object oninvoke) {

+        this.oninvoke = oninvoke;

+    }

+    

+    @Parameter(key = Constants.ON_INVOKE_METHOD_KEY, excluded = true, attribute = true)

+    public String getOninvokeMethod() {

+        return oninvokeMethod;

+    }

+    

+    public void setOninvokeMethod(String oninvokeMethod) {

+        this.oninvokeMethod = oninvokeMethod;

+    }

+

+    public Boolean isReturn() {

+        return isReturn;

+    }

+

+    public void setReturn(Boolean isReturn) {

+        this.isReturn = isReturn;

+    }

+

+    public String getMerger() {

+        return merger;

+    }

+

+    public void setMerger(String merger) {

+        this.merger = merger;

+    }    

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/MonitorConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/MonitorConfig.java
new file mode 100644
index 0000000..0992d0b
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/MonitorConfig.java
@@ -0,0 +1,112 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.Map;

+

+/**

+ * MonitorConfig

+ * 

+ * @author william.liangf

+ */

+public class MonitorConfig extends AbstractConfig {

+	

+	private static final long serialVersionUID = -1184681514659198203L;

+	

+	private String protocol;

+	

+	private String address;

+

+    private String username;

+

+    private String password;

+

+	private String group;

+

+    private String version;

+

+    // 自定义参数

+    private Map<String, String> parameters;

+

+    public MonitorConfig() {

+    }

+

+    public MonitorConfig(String address) {

+        this.address = address;

+    }

+

+    @Parameter(excluded = true)

+	public String getAddress() {

+		return address;

+	}

+

+	public void setAddress(String address) {

+		this.address = address;

+	}

+

+	@Parameter(excluded = true)

+    public String getProtocol() {

+        return protocol;

+    }

+

+    public void setProtocol(String protocol) {

+        this.protocol = protocol;

+    }

+

+    @Parameter(excluded = true)

+    public String getUsername() {

+        return username;

+    }

+

+    public void setUsername(String username) {

+        this.username = username;

+    }

+

+    @Parameter(excluded = true)

+    public String getPassword() {

+        return password;

+    }

+

+    public void setPassword(String password) {

+        this.password = password;

+    }

+

+    public String getGroup() {

+        return group;

+    }

+

+    public void setGroup(String group) {

+        this.group = group;

+    }

+

+    public String getVersion() {

+        return version;

+    }

+

+    public void setVersion(String version) {

+        this.version = version;

+    }

+

+    public Map<String, String> getParameters() {

+        return parameters;

+    }

+

+    public void setParameters(Map<String, String> parameters) {

+        checkParameterName(parameters);

+        this.parameters = parameters;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/Parameter.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/Parameter.java
new file mode 100644
index 0000000..17696fb
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/Parameter.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+/**

+ * Parameter

+ * 

+ * @author william.liangf

+ */

+@Documented

+@Retention(RetentionPolicy.RUNTIME)

+@Target({ElementType.METHOD})

+public @interface Parameter {

+

+    String key() default "";

+    

+    boolean required() default false;

+    

+    boolean excluded() default false;

+

+    boolean escaped() default false;

+    

+    boolean attribute() default false;

+

+    boolean append() default false;

+    

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
new file mode 100644
index 0000000..07c46cb
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
@@ -0,0 +1,407 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.Map;

+

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.serialize.Serialization;

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.common.threadpool.ThreadPool;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+import com.alibaba.dubbo.remoting.Dispather;

+import com.alibaba.dubbo.remoting.Codec;

+import com.alibaba.dubbo.remoting.Transporter;

+import com.alibaba.dubbo.remoting.exchange.Exchanger;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.rpc.Protocol;

+

+/**

+ * ProtocolConfig

+ * 

+ * @author william.liangf

+ */

+public class ProtocolConfig extends AbstractConfig {

+

+    private static final long   serialVersionUID = 6913423882496634749L;

+

+    // 服务协议

+    private String              name;

+

+    // 服务IP地址(多网卡时使用)

+    private String              host;

+

+    // 服务端口

+    private Integer             port;

+

+    // 上下文路径

+    private String              contextpath;

+    

+    // 线程池类型

+    private String              threadpool;

+    

+    // 线程池大小(固定大小)

+    private Integer             threads;

+    

+    // IO线程池大小(固定大小)

+    private Integer             iothreads;

+    

+    // 线程池队列大小

+    private Integer             queues;

+    

+    // 最大接收连接数

+    private Integer             accepts;

+    

+    // 协议编码

+    private String              codec;

+    

+    // 序列化方式

+    private String              serialization;

+    

+    // 字符集

+    private String              charset;

+    

+    // 最大请求数据长度

+    private Integer             payload;

+    

+    // 缓存区大小

+    private Integer             buffer;

+    

+    // 心跳间隔

+    private Integer             heartbeat;

+

+    // 访问日志

+    private String              accesslog;

+    

+    // 网络传输方式

+    private String              transporter;

+    

+    // 信息交换方式

+    private String              exchanger;

+    

+    // 信息线程模型派发方式

+    private String              dispather;

+

+    // 对称网络组网方式

+    private String              networker;

+    

+    // 服务器端实现

+    private String              server;

+    

+    // 客户端实现

+    private String              client;

+    

+    // 支持的telnet命令,多个命令用逗号分隔

+    private String              telnet;

+    

+    // 命令行提示符

+    private String              prompt;

+

+    // status检查

+    private String              status;

+    

+    // 是否注册

+    private Boolean             register;

+    

+    // 参数

+    private Map<String, String> parameters;

+    

+    public ProtocolConfig() {

+    }

+    

+    public ProtocolConfig(String name) {

+        setName(name);

+    }

+

+    public ProtocolConfig(String name, int port) {

+        setName(name);

+        setPort(port);

+    }

+    

+    @Parameter(excluded = true)

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        checkName("name", name);

+        this.name = name;

+        if (id == null || id.length() == 0) {

+            id = name;

+        }

+    }

+

+    @Parameter(excluded = true)

+    public String getHost() {

+        return host;

+    }

+

+    public void setHost(String host) {

+        checkName("host", host);

+        this.host = host;

+    }

+

+    @Parameter(excluded = true)

+    public Integer getPort() {

+        return port;

+    }

+

+    public void setPort(Integer port) {

+        this.port = port;

+    }

+

+    @Deprecated

+    @Parameter(excluded = true)

+    public String getPath() {

+        return getContextpath();

+    }

+

+    @Deprecated

+    public void setPath(String path) {

+        setContextpath(path);

+    }

+

+    @Parameter(excluded = true)

+    public String getContextpath() {

+        return contextpath;

+    }

+

+    public void setContextpath(String contextpath) {

+        checkPathName("contextpath", contextpath);

+        this.contextpath = contextpath;

+    }

+

+    public String getThreadpool() {

+        return threadpool;

+    }

+

+    public void setThreadpool(String threadpool) {

+        checkExtension(ThreadPool.class, "threadpool", threadpool);

+        this.threadpool = threadpool;

+    }

+

+    public Integer getThreads() {

+        return threads;

+    }

+

+    public void setThreads(Integer threads) {

+        this.threads = threads;

+    }

+

+    public Integer getIothreads() {

+        return iothreads;

+    }

+

+    public void setIothreads(Integer iothreads) {

+        this.iothreads = iothreads;

+    }

+

+    public Integer getQueues() {

+        return queues;

+    }

+    

+    public void setQueues(Integer queues) {

+        this.queues = queues;

+    }

+    

+    public Integer getAccepts() {

+        return accepts;

+    }

+    

+    public void setAccepts(Integer accepts) {

+        this.accepts = accepts;

+    }

+

+    public String getCodec() {

+        return codec;

+    }

+

+    public void setCodec(String codec) {

+        if ("dubbo".equals(name)) {

+            checkMultiExtension(Codec.class, "codec", codec);

+        }

+        this.codec = codec;

+    }

+

+    public String getSerialization() {

+        return serialization;

+    }

+    

+    public void setSerialization(String serialization) {

+        if ("dubbo".equals(name)) {

+            checkMultiExtension(Serialization.class, "serialization", serialization);

+        }

+        this.serialization = serialization;

+    }

+

+    public String getCharset() {

+        return charset;

+    }

+

+    public void setCharset(String charset) {

+        this.charset = charset;

+    }

+

+    public Integer getPayload() {

+        return payload;

+    }

+

+    public void setPayload(Integer payload) {

+        this.payload = payload;

+    }

+

+    public Integer getBuffer() {

+        return buffer;

+    }

+

+    public void setBuffer(Integer buffer) {

+        this.buffer = buffer;

+    }

+

+    public Integer getHeartbeat() {

+        return heartbeat;

+    }

+

+    public void setHeartbeat(Integer heartbeat) {

+        this.heartbeat = heartbeat;

+    }

+

+    public String getServer() {

+        return server;

+    }

+

+    public void setServer(String server) {

+        if ("dubbo".equals(name)) {

+            checkMultiExtension(Transporter.class, "server", server);

+        }

+        this.server = server;

+    }

+

+    public String getClient() {

+        return client;

+    }

+

+    public void setClient(String client) {

+        if ("dubbo".equals(name)) {

+            checkMultiExtension(Transporter.class, "client", client);

+        }

+        this.client = client;

+    }

+    

+    public String getAccesslog() {

+        return accesslog;

+    }

+    

+    public void setAccesslog(String accesslog) {

+        this.accesslog = accesslog;

+    }

+

+    public String getTelnet() {

+        return telnet;

+    }

+    

+    public void setTelnet(String telnet) {

+        checkMultiExtension(TelnetHandler.class, "telnet", telnet);

+        this.telnet = telnet;

+    }

+

+    @Parameter(escaped = true)

+    public String getPrompt() {

+        return prompt;

+    }

+

+    public void setPrompt(String prompt) {

+        this.prompt = prompt;

+    }

+

+    public String getStatus() {

+        return status;

+    }

+    

+    public void setStatus(String status) {

+        checkMultiExtension(StatusChecker.class, "status", status);

+        this.status = status;

+    }

+

+    public Boolean isRegister() {

+        return register;

+    }

+    

+    public void setRegister(Boolean register) {

+        this.register = register;

+    }

+    

+    public String getTransporter() {

+        return transporter;

+    }

+    

+    public void setTransporter(String transporter) {

+        checkExtension(Transporter.class, "transporter", transporter);

+        this.transporter = transporter;

+    }

+    

+    public String getExchanger() {

+        return exchanger;

+    }

+    

+    public void setExchanger(String exchanger) {

+        checkExtension(Exchanger.class, "exchanger", exchanger);

+        this.exchanger = exchanger;

+    }

+

+    public String getDispather() {

+        return dispather;

+    }

+

+    public void setDispather(String dispather) {

+        checkExtension(Dispather.class, "dispather", exchanger);

+        this.dispather = dispather;

+    }

+

+    public String getNetworker() {

+        return networker;

+    }

+

+    public void setNetworker(String networker) {

+        this.networker = networker;

+    }

+

+    public Map<String, String> getParameters() {

+        return parameters;

+    }

+

+    public void setParameters(Map<String, String> parameters) {

+        this.parameters = parameters;

+    }

+

+    public void destory() {

+        if (name != null) {

+            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).destroy();;

+        }

+    }

+

+    public static void destroyAll() {

+        AbstractRegistryFactory.destroyAll();

+        for (String protocol : ExtensionLoader.getExtensionLoader(Protocol.class).getLoadedExtensions()) {

+            try {

+                ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocol).destroy();

+            } catch (Throwable t) {

+                logger.warn(t.getMessage(), t);

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java
new file mode 100644
index 0000000..08b1830
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java
@@ -0,0 +1,376 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.Arrays;

+

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.common.threadpool.ThreadPool;

+import com.alibaba.dubbo.remoting.Dispather;

+import com.alibaba.dubbo.remoting.Transporter;

+import com.alibaba.dubbo.remoting.exchange.Exchanger;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+

+/**

+ * ProviderConfig

+ * 

+ * @see com.alibaba.dubbo.config.ProtocolConfig

+ * @see com.alibaba.dubbo.config.ServiceConfig

+ * @author william.liangf

+ */

+public class ProviderConfig extends AbstractServiceConfig {

+

+    private static final long   serialVersionUID = 6913423882496634749L;

+

+    // ======== 协议缺省值,当协议属性未设置时使用该缺省值替代  ========

+

+    // 服务IP地址(多网卡时使用)

+    private String              host;

+

+    // 服务端口

+    private Integer             port;

+

+    // 上下

+    private String              contextpath;

+

+    // 线程池类型

+    private String              threadpool;

+    

+    // 线程池大小(固定大小)

+    private Integer             threads;

+

+    // IO线程池大小(固定大小)

+    private Integer             iothreads;

+    

+    // 线程池队列大小

+    private Integer             queues;

+

+    // 最大接收连接数

+    private Integer             accepts;

+    

+    // 协议编码

+    private String              codec;

+    

+    // 序列化方式

+    private String              serialization;

+

+    // 字符集

+    private String              charset;

+    

+    // 最大请求数据长度

+    private Integer             payload;

+

+    // 缓存区大小

+    private Integer             buffer;

+    

+    // 网络传输方式

+    private String              transporter;

+    

+    // 信息交换方式

+    private String              exchanger;

+

+    // 信息线程模型派发方式

+    private String              dispather;

+

+    // 对称网络组网方式

+    private String              networker;

+    

+    // 服务器端实现

+    private String              server;

+    

+    // 客户端实现

+    private String              client;

+    

+    // 支持的telnet命令,多个命令用逗号分隔

+    private String              telnet;

+

+    // 命令行提示符

+    private String              prompt;

+

+    // status检查

+    private String              status;

+    

+    // 停止时等候时间

+    private Integer             wait;

+    

+    // 是否为缺省

+    private Boolean             isDefault;

+    

+    @Deprecated

+    public void setProtocol(String protocol) {

+        this.protocols = Arrays.asList(new ProtocolConfig[] {new ProtocolConfig(protocol)});

+    }

+

+    @Parameter(excluded = true)

+    public Boolean isDefault() {

+        return isDefault;

+    }

+

+    @Deprecated

+    public void setDefault(Boolean isDefault) {

+        this.isDefault = isDefault;

+    }

+    

+    @Parameter(excluded = true)

+    public String getHost() {

+        return host;

+    }

+    

+    public void setHost(String host) {

+        this.host = host;

+    }

+    

+    @Parameter(excluded = true)

+    public Integer getPort() {

+        return port;

+    }

+    

+    @Deprecated

+    public void setPort(Integer port) {

+        this.port = port;

+    }

+

+    @Deprecated

+    @Parameter(excluded = true)

+    public String getPath() {

+        return getContextpath();

+    }

+

+    @Deprecated

+    public void setPath(String path) {

+        setContextpath(path);

+    }

+

+    @Parameter(excluded = true)

+    public String getContextpath() {

+        return contextpath;

+    }

+

+    public void setContextpath(String contextpath) {

+        checkPathName("contextpath", contextpath);

+        this.contextpath = contextpath;

+    }

+

+    public String getThreadpool() {

+        return threadpool;

+    }

+

+    public void setThreadpool(String threadpool) {

+        checkExtension(ThreadPool.class, "threadpool", threadpool);

+        this.threadpool = threadpool;

+    }

+    

+    public Integer getThreads() {

+        return threads;

+    }

+    

+    public void setThreads(Integer threads) {

+        this.threads = threads;

+    }

+

+    public Integer getIothreads() {

+        return iothreads;

+    }

+

+    public void setIothreads(Integer iothreads) {

+        this.iothreads = iothreads;

+    }

+

+    public Integer getQueues() {

+        return queues;

+    }

+    

+    public void setQueues(Integer queues) {

+        this.queues = queues;

+    }

+    

+    public Integer getAccepts() {

+        return accepts;

+    }

+    

+    public void setAccepts(Integer accepts) {

+        this.accepts = accepts;

+    }

+

+    public String getCodec() {

+        return codec;

+    }

+

+    public void setCodec(String codec) {

+        this.codec = codec;

+    }

+

+    public String getSerialization() {

+        return serialization;

+    }

+    

+    public void setSerialization(String serialization) {

+        this.serialization = serialization;

+    }

+

+    public String getCharset() {

+        return charset;

+    }

+

+    public void setCharset(String charset) {

+        this.charset = charset;

+    }

+

+    public Integer getPayload() {

+        return payload;

+    }

+    

+    public void setPayload(Integer payload) {

+        this.payload = payload;

+    }

+

+    public Integer getBuffer() {

+        return buffer;

+    }

+

+    public void setBuffer(Integer buffer) {

+        this.buffer = buffer;

+    }

+

+    public String getServer() {

+        return server;

+    }

+

+    public void setServer(String server) {

+        this.server = server;

+    }

+    

+    public String getClient() {

+        return client;

+    }

+

+    public void setClient(String client) {

+        this.client = client;

+    }

+

+    public String getTelnet() {

+        return telnet;

+    }

+    

+    public void setTelnet(String telnet) {

+        checkMultiExtension(TelnetHandler.class, "telnet", telnet);

+        this.telnet = telnet;

+    }

+

+    @Parameter(escaped = true)

+    public String getPrompt() {

+        return prompt;

+    }

+

+    public void setPrompt(String prompt) {

+        this.prompt = prompt;

+    }

+

+    public String getStatus() {

+        return status;

+    }

+    

+    public void setStatus(String status) {

+        checkMultiExtension(StatusChecker.class, "status", status);

+        this.status = status;

+    }

+

+    @Parameter(key = "default.cluster")

+    @Override

+    public String getCluster() {

+        return super.getCluster();

+    }

+

+    @Parameter(key = "default.connections")

+    @Override

+    public Integer getConnections() {

+        return super.getConnections();

+    }

+

+    @Parameter(key = "default.timeout")

+    @Override

+    public Integer getTimeout() {

+        return super.getTimeout();

+    }

+

+    @Parameter(key = "default.retries")

+    @Override

+    public Integer getRetries() {

+        return super.getRetries();

+    }

+

+    @Parameter(key = "default.loadbalance")

+    @Override

+    public String getLoadbalance() {

+        return super.getLoadbalance();

+    }

+

+    @Parameter(key = "default.async")

+    @Override

+    public Boolean isAsync() {

+        return super.isAsync();

+    }

+

+    @Parameter(key = "default.actives")

+    @Override

+    public Integer getActives() {

+        return super.getActives();

+    }

+    

+    public String getTransporter() {

+        return transporter;

+    }

+    

+    public void setTransporter(String transporter) {

+        checkExtension(Transporter.class, "transporter", transporter);

+        this.transporter = transporter;

+    }

+    

+    public String getExchanger() {

+        return exchanger;

+    }

+    

+    public void setExchanger(String exchanger) {

+        checkExtension(Exchanger.class, "exchanger", exchanger);

+        this.exchanger = exchanger;

+    }

+

+    public String getDispather() {

+        return dispather;

+    }

+

+    public void setDispather(String dispather) {

+        checkExtension(Dispather.class, "dispather", exchanger);

+        this.dispather = dispather;

+    }

+

+    public String getNetworker() {

+        return networker;

+    }

+

+    public void setNetworker(String networker) {

+        this.networker = networker;

+    }

+

+    public Integer getWait() {

+        return wait;

+    }

+    

+    public void setWait(Integer wait) {

+        this.wait = wait;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java
new file mode 100644
index 0000000..2f60751
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java
@@ -0,0 +1,471 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.io.File;

+import java.io.FileInputStream;

+import java.io.IOException;

+import java.lang.reflect.Method;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Properties;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.bytecode.Wrapper;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.StaticContext;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;

+import com.alibaba.dubbo.rpc.cluster.support.AvailableCluster;

+import com.alibaba.dubbo.rpc.cluster.support.ClusterUtils;

+import com.alibaba.dubbo.rpc.service.GenericService;

+

+/**

+ * ReferenceConfig

+ * 

+ * @author william.liangf

+ */

+public class ReferenceConfig<T> extends AbstractConsumerConfig {

+

+    private static final long    serialVersionUID        = -5864351140409987595L;

+

+    private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+

+    private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();

+    

+    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+

+    // 接口类型

+    private String               interfaceName;

+

+    private Class<?>             interfaceClass;

+

+    // 版本

+    private String               version;

+

+    // 服务分组

+    private String               group;

+

+    // 客户端类型

+    private String               client;

+

+    // 点对点直连服务提供地址

+    private String               url;

+

+    // 方法配置

+    private List<MethodConfig>   methods;

+

+    // 缺省配置

+    private ConsumerConfig       consumer;

+

+    // 接口代理类引用

+    private transient T          ref;

+

+    private transient Invoker<?> invoker;

+

+    private transient boolean    initialized;

+

+    private transient boolean    destroyed;

+

+    private final List<URL> urls = new ArrayList<URL>();

+    

+    public List<URL> toUrls() {

+        return urls;

+    }

+

+    public synchronized T get() {

+        if (destroyed){

+            throw new IllegalStateException("Already destroyed!");

+        }

+    	if (ref == null) {

+    		init();

+    	}

+    	return ref;

+    }

+    

+    public synchronized void destroy() {

+        if (ref == null) {

+            return;

+        }

+        if (destroyed){

+            return;

+        }

+        destroyed = true;

+        try {

+            invoker.destroy();

+        } catch (Throwable t) {

+            logger.warn("Unexpected err when destroy invoker of ReferenceConfig(" + url + ").", t);

+        }

+        invoker = null;

+        ref = null;

+    }

+

+    private void init() {

+	    if (initialized) {

+	        return;

+	    }

+	    initialized = true;

+    	if (interfaceName == null || interfaceName.length() == 0) {

+    	    throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");

+    	}

+    	// 获取消费者全局配置

+    	checkDefault();

+        if (generic == null && consumer != null) {

+            generic = consumer.isGeneric();

+        }

+        if (generic == null) {

+        	generic = false;

+        }

+        if (generic) {

+            interfaceClass = GenericService.class;

+        } else {

+            try {

+				interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()

+				        .getContextClassLoader());

+			} catch (ClassNotFoundException e) {

+				throw new IllegalStateException(e.getMessage(), e);

+			}

+            checkInterfaceAndMethods(interfaceClass, methods);

+        }

+        String resolve = System.getProperty(interfaceName);

+        String resolveFile = null;

+        if (resolve == null || resolve.length() == 0) {

+	        resolveFile = System.getProperty("dubbo.resolve.file");

+	        if (resolveFile == null || resolveFile.length() == 0) {

+	        	File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");

+	        	if (userResolveFile.exists()) {

+	        		resolveFile = userResolveFile.getAbsolutePath();

+	        	}

+	        }

+	        if (resolveFile != null && resolveFile.length() > 0) {

+	        	Properties properties = new Properties();

+	        	FileInputStream fis = null;

+	        	try {

+	        	    fis = new FileInputStream(new File(resolveFile));

+					properties.load(fis);

+				} catch (IOException e) {

+					throw new IllegalStateException("Unload " + resolveFile + ", cause: " + e.getMessage(), e);

+				} finally {

+				    try {

+                        if(null != fis) fis.close();

+                    } catch (IOException e) {

+                        logger.warn(e.getMessage(), e);

+                    }

+				}

+	        	resolve = properties.getProperty(interfaceName);

+	        }

+        }

+        if (resolve != null && resolve.length() > 0) {

+        	url = resolve;

+        	if (logger.isWarnEnabled()) {

+        		if (resolveFile != null && resolveFile.length() > 0) {

+        			logger.warn("Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");

+        		} else {

+        			logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");

+        		}

+    		}

+        }

+        if (consumer != null) {

+            if (application == null) {

+                application = consumer.getApplication();

+            }

+            if (registries == null) {

+                registries = consumer.getRegistries();

+            }

+            if (monitor == null) {

+                monitor = consumer.getMonitor();

+            }

+        }

+        if (application != null) {

+            if (registries == null) {

+                registries = application.getRegistries();

+            }

+            if (monitor == null) {

+                monitor = application.getMonitor();

+            }

+        }

+        checkApplication();

+        checkStubAndMock(interfaceClass);

+        Map<String, String> map = new HashMap<String, String>();

+        Map<Object, Object> attributes = new HashMap<Object, Object>();

+        map.put("dubbo", Version.getVersion());

+        // map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));

+        if (! generic) {

+            String revision = Version.getVersion(interfaceClass, version);

+            if (revision != null && revision.length() > 0) {

+                map.put("revision", revision);

+            }

+            map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(interfaceClass).getDeclaredMethodNames())), ","));

+        }

+        map.put(Constants.INTERFACE_KEY, interfaceName);

+        appendParameters(map, application);

+        appendParameters(map, consumer, Constants.DEFAULT_KEY);

+        appendParameters(map, this);

+        String prifix = StringUtils.getServiceKey(map);

+        if (methods != null && methods.size() > 0) {

+            for (MethodConfig method : methods) {

+                appendParameters(map, method, method.getName());

+                String retryKey = method.getName() + ".retry";

+                if (map.containsKey(retryKey)) {

+                    String retryValue = map.remove(retryKey);

+                    if ("false".equals(retryValue)) {

+                        map.put(method.getName() + ".retries", "0");

+                    }

+                }

+                appendAttributes(attributes, method, prifix + "." + method.getName());

+                checkAndConvertImplicitConfig(method, map, attributes);

+            }

+        }

+        //attributes通过系统context进行存储.

+        StaticContext.getSystemContext().putAll(attributes);

+        ref = createProxy(map);

+    }

+    

+    private static void checkAndConvertImplicitConfig(MethodConfig method, Map<String,String> map, Map<Object,Object> attributes){

+      //check config conflict

+        if (Boolean.FALSE.equals(method.isReturn()) && (method.getOnreturn() != null || method.getOnthrow() != null)) {

+            throw new IllegalStateException("method config error : return attribute must be set true when onreturn or onthrow has been setted.");

+        }

+        //convert onreturn methodName to Method

+        String onReturnMethodKey = StaticContext.getKey(map,method.getName(),Constants.ON_RETURN_METHOD_KEY);

+        Object onReturnMethod = attributes.get(onReturnMethodKey);

+        if (onReturnMethod != null && onReturnMethod instanceof String){

+            attributes.put(onReturnMethodKey, getMethodByName(method.getOnreturn().getClass(), onReturnMethod.toString()));

+        }

+        //convert onthrow methodName to Method

+        String onThrowMethodKey = StaticContext.getKey(map,method.getName(),Constants.ON_THROW_METHOD_KEY);

+        Object onThrowMethod = attributes.get(onThrowMethodKey);

+        if (onThrowMethod != null && onThrowMethod instanceof String){

+            attributes.put(onThrowMethodKey, getMethodByName(method.getOnthrow().getClass(), onThrowMethod.toString()));

+        }

+        //convert oninvoke methodName to Method

+        String onInvokeMethodKey = StaticContext.getKey(map,method.getName(),Constants.ON_INVOKE_METHOD_KEY);

+        Object onInvokeMethod = attributes.get(onInvokeMethodKey);

+        if (onInvokeMethod != null && onInvokeMethod instanceof String){

+            attributes.put(onInvokeMethodKey, getMethodByName(method.getOninvoke().getClass(), onInvokeMethod.toString()));

+        }

+    }

+

+    private static Method getMethodByName(Class<?> clazz, String methodName){

+        try {

+            return ReflectUtils.findMethodByMethodName(clazz, methodName);

+        } catch (Exception e) {

+            throw new IllegalStateException(e);

+        }

+    }

+    

+	@SuppressWarnings({ "unchecked", "rawtypes" })

+	private T createProxy(Map<String, String> map) {

+	    Boolean j = injvm;

+        if (j == null && consumer != null) {

+            j = consumer.isInjvm();

+        }

+        if (j != null && j) {

+            URL url = new URL("injvm", NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);

+            invoker = protocol.refer(interfaceClass, url);

+            if (logger.isInfoEnabled()) {

+                logger.info("Using injvm service " + interfaceClass.getName());

+            }

+        } else {

+            if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL

+                String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);

+                if (us != null && us.length > 0) {

+                    for (String u : us) {

+                        URL url = URL.valueOf(u);

+                        if (url.getPath() == null || url.getPath().length() == 0) {

+                            url = url.setPath(interfaceName);

+                        }

+                        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {

+                            urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));

+                        } else {

+                            urls.add(ClusterUtils.mergeUrl(url, map));

+                        }

+                    }

+                }

+            } else { // 通过注册中心配置拼装URL

+            	List<URL> us = loadRegistries();

+            	if (us != null && us.size() > 0) {

+                	for (URL u : us) {

+                	    URL monitorUrl = loadMonitor(u);

+                        if (monitorUrl != null) {

+                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));

+                        }

+                	    urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));

+                    }

+            	}

+            	if (urls == null || urls.size() == 0) {

+                    throw new IllegalStateException("No such any registry to reference " + interfaceName  + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");

+                }

+            }

+            if (urls.size() == 1) {

+                invoker = protocol.refer(interfaceClass, urls.get(0));

+            } else {

+                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();

+                URL registryURL = null;

+                for (URL url : urls) {

+                    invokers.add(protocol.refer(interfaceClass, url));

+                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {

+                        registryURL = url; // 用了最后一个registry url

+                    }

+                }

+                if (registryURL != null) { // 有 注册中心协议的URL

+                    // 对有注册中心的Cluster 只用 AvailableCluster

+                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME); 

+                    invoker = cluster.join(new StaticDirectory(u, invokers));

+                }  else { // 不是 注册中心的URL

+                    invoker = cluster.join(new StaticDirectory(invokers));

+                }

+            }

+        }

+        Boolean c = check;

+        if (c == null && consumer != null) {

+            c = consumer.isCheck();

+        }

+        if (c == null) {

+            c = true; // default true

+        }

+        if (c && ! invoker.isAvailable()) {

+            throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());

+        }

+        if (logger.isInfoEnabled()) {

+            logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());

+        }

+        // 创建服务代理

+        return (T) proxyFactory.getProxy(invoker);

+    }

+

+    private void checkDefault() {

+        if (consumer == null) {

+            consumer = new ConsumerConfig();

+        }

+        appendProperties(consumer);

+    }

+

+	public Class<?> getInterfaceClass() {

+	    if (interfaceClass != null) {

+	        return interfaceClass;

+	    }

+	    if ((generic != null && generic.booleanValue())

+	            || (consumer != null && consumer.isGeneric() != null 

+	                && consumer.isGeneric().booleanValue())) {

+	        return GenericService.class;

+	    }

+	    try {

+	        if (interfaceName != null && interfaceName.length() > 0) {

+	            this.interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()

+                    .getContextClassLoader());

+	        }

+        } catch (ClassNotFoundException t) {

+            throw new IllegalStateException(t.getMessage(), t);

+        }

+	    return interfaceClass;

+	}

+	

+	/**

+	 * @deprecated

+	 * @see #setInterface(Class)

+	 * @param interfaceClass

+	 */

+	@Deprecated

+	public void setInterfaceClass(Class<?> interfaceClass) {

+	    setInterface(interfaceClass);

+	}

+

+    public String getInterface() {

+        return interfaceName;

+    }

+

+    public void setInterface(String interfaceName) {

+        this.interfaceName = interfaceName;

+        if (id == null || id.length() == 0) {

+            id = interfaceName;

+        }

+    }

+    

+    public void setInterface(Class<?> interfaceClass) {

+        if (interfaceClass != null && ! interfaceClass.isInterface()) {

+            throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");

+        }

+        this.interfaceClass = interfaceClass;

+        setInterface(interfaceClass == null ? (String) null : interfaceClass.getName());

+    }

+    

+    public String getVersion() {

+        return version;

+    }

+

+    public void setVersion(String version) {

+        checkName("version", version);

+        this.version = version;

+    }

+

+    public String getGroup() {

+        return group;

+    }

+

+    public void setGroup(String group) {

+        checkKey("group", group);

+        this.group = group;

+    }

+

+    public String getClient() {

+        return client;

+    }

+

+    public void setClient(String client) {

+        checkName("client", client);

+        this.client = client;

+    }

+

+    @Parameter(excluded = true)

+    public String getUrl() {

+        return url;

+    }

+

+    public void setUrl(String url) {

+        this.url = url;

+    }

+

+    public List<MethodConfig> getMethods() {

+        return methods;

+    }

+

+    @SuppressWarnings("unchecked")

+    public void setMethods(List<? extends MethodConfig> methods) {

+        this.methods = (List<MethodConfig>) methods;

+    }

+

+    public ConsumerConfig getConsumer() {

+        return consumer;

+    }

+

+    public void setConsumer(ConsumerConfig consumer) {

+        this.consumer = consumer;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
new file mode 100644
index 0000000..2224667
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
@@ -0,0 +1,305 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+import com.alibaba.dubbo.remoting.Transporter;

+

+/**

+ * RegistryConfig

+ * 

+ * @author william.liangf

+ */

+public class RegistryConfig extends AbstractConfig {

+

+	private static final long serialVersionUID = 5508512956753757169L;

+	

+	public static final String NO_AVAILABLE = "N/A";

+

+    // 注册中心地址

+    private String            address;

+    

+	// 注册中心登录用户名

+    private String            username;

+

+    // 注册中心登录密码

+    private String            password;

+

+    // 注册中心缺省端口

+    private Integer           port;

+    

+    // 注册中心协议

+    private String            protocol;

+

+    // 客户端实现

+    private String            transporter;

+    

+    private String            server;

+    

+    private String            client;

+

+    private String            group;

+    

+    private String            version;

+

+    // 注册中心请求超时时间(毫秒)

+    private Integer           timeout;

+

+    // 注册中心会话超时时间(毫秒)

+    private Integer           session;

+    

+    // 动态注册中心列表存储文件

+    private String            file;

+    

+    // 停止时等候完成通知时间

+    private Integer           wait;

+    

+    // 启动时检查注册中心是否存在

+    private Boolean           check;

+

+    // 在该注册中心上注册是动态的还是静态的服务

+    private Boolean           dynamic;

+    

+    // 在该注册中心上服务是否暴露

+    private Boolean           register;

+    

+    // 在该注册中心上服务是否引用

+    private Boolean           subscribe;

+

+    // 自定义参数

+    private Map<String, String> parameters;

+

+    public RegistryConfig() {

+    }

+    

+    public RegistryConfig(String address) {

+        setAddress(address);

+    }

+

+    public String getProtocol() {

+        return protocol;

+    }

+

+    public void setProtocol(String protocol) {

+        checkName("protocol", protocol);

+        this.protocol = protocol;

+    }

+

+    @Parameter(excluded = true)

+    public String getAddress() {

+        return address;

+    }

+

+    public void setAddress(String address) {

+        this.address = address;

+    }

+

+    public Integer getPort() {

+        return port;

+    }

+

+    public void setPort(Integer port) {

+        this.port = port;

+    }

+

+    public String getUsername() {

+        return username;

+    }

+

+    public void setUsername(String username) {

+        checkName("username", username);

+        this.username = username;

+    }

+

+    public String getPassword() {

+        return password;

+    }

+

+    public void setPassword(String password) {

+        checkLength("password", password);

+        this.password = password;

+    }

+

+    /**

+     * @deprecated

+     * @see com.alibaba.dubbo.config.ProviderConfig#getWait()

+     * @return wait

+     */

+    @Deprecated

+    public Integer getWait() {

+        return wait;

+    }

+

+    /**

+     * @deprecated

+     * @see com.alibaba.dubbo.config.ProviderConfig#setWait(Integer)

+     * @param wait

+     */

+    @Deprecated

+    public void setWait(Integer wait) {

+        this.wait = wait;

+        if( wait!=null && wait>0)

+            System.setProperty(Constants.SHUTDOWN_WAIT_KEY, String.valueOf(wait));

+    }

+    

+    public Boolean isCheck() {

+		return check;

+	}

+

+	public void setCheck(Boolean check) {

+		this.check = check;

+	}

+

+    public String getFile() {

+        return file;

+    }

+

+    public void setFile(String file) {

+        checkPathLength("file", file);

+        this.file = file;

+    }

+

+    /**

+     * @deprecated

+     * @see #getTransporter()

+     * @return transport

+     */

+    @Deprecated

+    @Parameter(excluded = true)

+    public String getTransport() {

+        return getTransporter();

+    }

+    

+    /**

+     * @deprecated

+     * @see #setTransporter(String)

+     * @param transport

+     */

+    @Deprecated

+    public void setTransport(String transport) {

+        setTransporter(transport);

+    }

+    

+    public String getTransporter() {

+        return transporter;

+    }

+

+    public void setTransporter(String transporter) {

+        checkName("transporter", transporter);

+        if(transporter != null && transporter.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(transporter)){

+            throw new IllegalStateException("No such transporter type : " + transporter);

+        }

+        this.transporter = transporter;

+    }

+    

+    public String getServer() {

+        return server;

+    }

+    

+    public void setServer(String server) {

+        checkName("server", server);

+        if(server != null && server.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(server)){

+            throw new IllegalStateException("No such server type : " + server);

+        }

+        this.server = server;

+    }

+    

+    public String getClient() {

+        return client;

+    }

+    

+    public void setClient(String client) {

+        checkName("client", client);

+        if(client != null && client.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(client)){

+            throw new IllegalStateException("No such client type : " + client);

+        }

+        this.client = client;

+    }

+

+	public Integer getTimeout() {

+		return timeout;

+	}

+

+	public void setTimeout(Integer timeout) {

+		this.timeout = timeout;

+	}

+

+    public Integer getSession() {

+        return session;

+    }

+

+    public void setSession(Integer session) {

+        this.session = session;

+    }

+

+    public Boolean isDynamic() {

+        return dynamic;

+    }

+

+    public void setDynamic(Boolean dynamic) {

+        this.dynamic = dynamic;

+    }

+

+    public Boolean isRegister() {

+        return register;

+    }

+

+    public void setRegister(Boolean register) {

+        this.register = register;

+    }

+    

+    public Boolean isSubscribe() {

+        return subscribe;

+    }

+    

+    public void setSubscribe(Boolean subscribe) {

+        this.subscribe = subscribe;

+    }

+

+    public static void closeAll() {

+        AbstractRegistryFactory.destroyAll();

+    }

+

+    public String getGroup() {

+        return group;

+    }

+

+    public void setGroup(String group) {

+        this.group = group;

+    }

+

+    public String getVersion() {

+        return version;

+    }

+

+    public void setVersion(String version) {

+        this.version = version;

+    }

+

+    public Map<String, String> getParameters() {

+        return parameters;

+    }

+

+    public void setParameters(Map<String, String> parameters) {

+        checkParameterName(parameters);

+        this.parameters = parameters;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
new file mode 100644
index 0000000..8e83ef7
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
@@ -0,0 +1,595 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.lang.reflect.Method;

+import java.net.InetAddress;

+import java.net.InetSocketAddress;

+import java.net.Socket;

+import java.net.SocketAddress;

+import java.net.UnknownHostException;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.UUID;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.bytecode.Wrapper;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.service.GenericService;

+

+/**

+ * ServiceConfig

+ * 

+ * @author william.liangf

+ */

+public class ServiceConfig<T> extends AbstractServiceConfig {

+

+    private static final long   serialVersionUID = 3033787999037024738L;

+

+    private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+    

+    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+

+    // 接口类型

+    private String              interfaceName;

+

+    private Class<?>            interfaceClass;

+

+    // 接口实现类引用

+    private T                   ref;

+

+    // 服务名称

+    private String              path;

+    

+    // 是否注册

+    private Boolean             register;

+

+    // 方法配置

+    private List<MethodConfig>  methods;

+

+    private ProviderConfig provider;

+

+    private final List<URL> urls = new ArrayList<URL>();

+    

+    private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();

+

+    private transient boolean exported;

+    

+    private transient boolean unexported;

+    

+    private transient boolean generic;

+    

+    public List<URL> toUrls() {

+        return urls;

+    }

+

+    public synchronized void export() {

+        if (delay != null && delay > 0) {

+            Thread thread = new Thread(new Runnable() {

+                public void run() {

+                    try {

+                        Thread.sleep(delay);

+                    } catch (Throwable e) {

+                    }

+                    doExport();

+                }

+            });

+            thread.setDaemon(true);

+            thread.setName("DelayExportServiceThread");

+            thread.start();

+        } else {

+            doExport();

+        }

+    }

+    

+    protected synchronized void doExport() {

+        if (unexported) {

+            throw new IllegalStateException("Already unexported!");

+        }

+        if (exported) {

+            return;

+        }

+        if (interfaceName == null || interfaceName.length() == 0) {

+            throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");

+        }

+        checkDefault();

+        if (provider != null) {

+            if (application == null) {

+                application = provider.getApplication();

+            }

+            if (registries == null) {

+                registries = provider.getRegistries();

+            }

+            if (monitor == null) {

+                monitor = provider.getMonitor();

+            }

+            if (protocols == null) {

+                protocols = provider.getProtocols();

+            }

+        }

+        if (application != null) {

+            if (registries == null) {

+                registries = application.getRegistries();

+            }

+            if (monitor == null) {

+                monitor = application.getMonitor();

+            }

+        }

+        if (ref instanceof GenericService) {

+            interfaceClass = GenericService.class;

+            generic = true;

+        } else {

+            try {

+                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()

+                        .getContextClassLoader());

+            } catch (ClassNotFoundException e) {

+                throw new IllegalStateException(e.getMessage(), e);

+            }

+            checkInterfaceAndMethods(interfaceClass, methods);

+            checkRef();

+            generic = false;

+        }

+        if(local !=null){

+            if(local=="true"){

+                local=interfaceName+"Local";

+            }

+            Class<?> localClass;

+            try {

+                localClass = Class.forName(local);

+            } catch (ClassNotFoundException e) {

+                throw new IllegalStateException(e.getMessage(), e);

+            }

+            if(!interfaceClass.isAssignableFrom(localClass)){

+                throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceName);

+            }

+        }

+        if(stub !=null){

+            if(stub=="true"){

+                stub=interfaceName+"Stub";

+            }

+            Class<?> stubClass;

+            try {

+                stubClass = Class.forName(stub);

+            } catch (ClassNotFoundException e) {

+                throw new IllegalStateException(e.getMessage(), e);

+            }

+            if(!interfaceClass.isAssignableFrom(stubClass)){

+                throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + interfaceName);

+            }

+        }

+        checkApplication();

+        checkRegistry();

+        checkProtocol();

+        checkStubAndMock(interfaceClass);

+        if (path == null || path.length() == 0) {

+            path = interfaceName;

+        }

+        doExportUrls();

+        exported = true;

+    }

+

+    private void checkRef() {

+        // 检查引用不为空,并且引用必需实现接口

+        if (ref == null) {

+            throw new IllegalStateException("ref not allow null!");

+        }

+        if (! interfaceClass.isInstance(ref)) {

+            throw new IllegalStateException("The class "

+                    + ref.getClass().getName() + " unimplemented interface "

+                    + interfaceClass + "!");

+        }

+    }

+

+    public synchronized void unexport() {

+        if (! exported) {

+            return;

+        }

+        if (unexported) {

+            return;

+        }

+    	if (exporters != null && exporters.size() > 0) {

+    		for (Exporter<?> exporter : exporters) {

+    			try {

+                    exporter.unexport();

+                } catch (Throwable t) {

+                    logger.warn("unexpected err when unexport" + exporter, t);

+                }

+    		}

+    		exporters.clear();

+    	}

+        unexported = true;

+    }

+    

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    private void doExportUrls() {

+        List<URL> registryURLs = loadRegistries();

+        for (ProtocolConfig protocolConfig : protocols) {

+            String name = protocolConfig.getName();

+            if (name == null || name.length() == 0) {

+                name = "dubbo";

+            }

+            String host = protocolConfig.getHost();

+            if (provider != null && (host == null || host.length() == 0)) {

+                host = provider.getHost();

+            }

+            boolean anyhost = false;

+            if (NetUtils.isInvalidLocalHost(host)) {

+                anyhost = true;

+                try {

+                    host = InetAddress.getLocalHost().getHostAddress();

+                } catch (UnknownHostException e) {

+                    logger.warn(e.getMessage(), e);

+                }

+                if (NetUtils.isInvalidLocalHost(host)) {

+                    if (registryURLs != null && registryURLs.size() > 0) {

+                        for (URL registryURL : registryURLs) {

+                            try {

+                                Socket socket = new Socket();

+                                try {

+                                    SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());

+                                    socket.connect(addr, 1000);

+                                    host = socket.getLocalAddress().getHostAddress();

+                                    break;

+                                } finally {

+                                    try {

+                                        socket.close();

+                                    } catch (Throwable e) {}

+                                }

+                            } catch (Exception e) {

+                                logger.warn(e.getMessage(), e);

+                            }

+                        }

+                    }

+                    if (NetUtils.isInvalidLocalHost(host)) {

+                        host = NetUtils.getLocalHost();

+                    }

+                }

+            }

+            Integer port = protocolConfig.getPort();

+            if (provider != null && (port == null || port == 0)) {

+                port = provider.getPort();

+            }

+            if (port == null || port == 0) {

+                port = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();

+            }

+            if (port == null || port <= 0) {

+                port = NetUtils.getAvailablePort();

+                logger.warn("Use random available port(" + port + ") for protocol " + name);

+            }

+            Map<String, String> map = new HashMap<String, String>();

+            if (anyhost) {

+                map.put(Constants.ANYHOST_KEY, "true");

+            }

+            map.put("dubbo", Version.getVersion());

+            // map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));

+            appendParameters(map, application);

+            appendParameters(map, provider);

+            appendParameters(map, protocolConfig);

+            appendParameters(map, this);

+            if (methods != null && methods.size() > 0) {

+                for (MethodConfig method : methods) {

+                    appendParameters(map, method, method.getName());

+                    String retryKey = method.getName() + ".retry";

+                    if (map.containsKey(retryKey)) {

+                        String retryValue = map.remove(retryKey);

+                        if ("false".equals(retryValue)) {

+                            map.put(method.getName() + ".retries", "0");

+                        }

+                    }

+                    List<ArgumentConfig> arguments = method.getArguments();

+                    if (arguments != null && arguments.size() > 0) {

+                        for (ArgumentConfig argument : arguments) {

+                            //类型自动转换.

+                            if(argument.getType() != null && argument.getType().length() >0){

+                                Method[] methods = interfaceClass.getMethods();

+                                //遍历所有方法

+                                if(methods != null && methods.length > 0){

+                                    for (int i = 0; i < methods.length; i++) {

+                                        String methodName = methods[i].getName();

+                                        //匹配方法名称,获取方法签名.

+                                        if(methodName.equals(method.getName())){

+                                            Class<?>[] argtypes = methods[i].getParameterTypes();

+                                            //一个方法中单个callback

+                                            if (argument.getIndex() != -1 ){

+                                                if (argtypes[argument.getIndex()].getName().equals(argument.getType())){

+                                                    appendParameters(map, argument, method.getName() + "." + argument.getIndex());

+                                                }else {

+                                                    throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());

+                                                }

+                                            } else {

+                                                //一个方法中多个callback

+                                                for (int j = 0 ;j<argtypes.length ;j++) {

+                                                    Class<?> argclazz = argtypes[j];

+                                                    if (argclazz.getName().equals(argument.getType())){

+                                                        appendParameters(map, argument, method.getName() + "." + j);

+                                                        if (argument.getIndex() != -1 && argument.getIndex() != j){

+                                                            throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());

+                                                        }

+                                                    }

+                                                }

+                                            }

+                                        }

+                                    }

+                                }

+                            }else if(argument.getIndex() != -1){

+                                appendParameters(map, argument, method.getName() + "." + argument.getIndex());

+                            }else {

+                                throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");

+                            }

+                            

+                        }

+                    }

+                }

+            }

+            if (generic) {

+                map.put("generic", String.valueOf(true));

+                map.put("methods", Constants.ANY_VALUE);

+            } else {

+                String revision = Version.getVersion(interfaceClass, version);

+                if (revision != null && revision.length() > 0) {

+                    map.put("revision", revision);

+                }

+                map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(interfaceClass).getDeclaredMethodNames())), ","));

+            }

+            if (! ConfigUtils.isEmpty(token)) {

+                if (ConfigUtils.isDefault(token)) {

+                    map.put("token", UUID.randomUUID().toString());

+                } else {

+                    map.put("token", token);

+                }

+            }

+            if ("injvm".equals(protocolConfig.getName())) {

+                protocolConfig.setRegister(false);

+                map.put("notify", "false");

+            }

+            // 导出服务

+            String contextPath = protocolConfig.getContextpath();

+            if ((contextPath == null || contextPath.length() == 0) && provider != null) {

+                contextPath = provider.getContextpath();

+            }

+            URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);

+            if (logger.isInfoEnabled()) {

+                logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);

+            }

+            if (registryURLs != null && registryURLs.size() > 0

+                    && url.getParameter("register", true)) {

+                for (URL registryURL : registryURLs) {

+                    URL monitorUrl = loadMonitor(registryURL);

+                    if (monitorUrl != null) {

+                        url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());

+                    }

+                    String providerURL = url.toFullString();

+                    if (logger.isInfoEnabled()) {

+                        logger.info("Register dubbo service " + interfaceClass.getName() + " url " + providerURL + " to registry " + registryURL);

+                    }

+                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, providerURL));

+                    Exporter<?> exporter = protocol.export(invoker);

+                    exporters.add(exporter);

+                }

+            } else {

+                Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

+                Exporter<?> exporter = protocol.export(invoker);

+                exporters.add(exporter);

+            }

+            this.urls.add(url);

+        }

+    }

+

+    private void checkDefault() {

+        if (provider == null) {

+            provider = new ProviderConfig();

+        }

+        appendProperties(provider);

+    }

+

+    private void checkProtocol() {

+        if ((protocols == null || protocols.size() == 0)

+                && provider != null) {

+            setProtocols(provider.getProtocols());

+        }

+    	// 兼容旧版本

+        if (protocols == null || protocols.size() == 0) {

+            setProtocol(new ProtocolConfig());

+        }

+        for (ProtocolConfig protocolConfig : protocols) {

+            appendProperties(protocolConfig);

+        }

+    }

+

+    public Class<?> getInterfaceClass() {

+        if (interfaceClass != null) {

+            return interfaceClass;

+        }

+        if (ref instanceof GenericService) {

+            return GenericService.class;

+        }

+        try {

+            if (interfaceName != null && interfaceName.length() > 0) {

+                this.interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()

+                    .getContextClassLoader());

+            }

+        } catch (ClassNotFoundException t) {

+            throw new IllegalStateException(t.getMessage(), t);

+        }

+        return interfaceClass;

+    }

+

+    /**

+     * @deprecated

+     * @see #setInterface(Class)

+     * @param interfaceClass

+     */

+    public void setInterfaceClass(Class<?> interfaceClass) {

+        setInterface(interfaceClass);

+    }

+

+    public String getInterface() {

+        return interfaceName;

+    }

+

+    public void setInterface(String interfaceName) {

+        this.interfaceName = interfaceName;

+        if (id == null || id.length() == 0) {

+            id = interfaceName;

+        }

+    }

+    

+    public void setInterface(Class<?> interfaceClass) {

+        if (interfaceClass != null && ! interfaceClass.isInterface()) {

+            throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");

+        }

+        this.interfaceClass = interfaceClass;

+        setInterface(interfaceClass == null ? (String) null : interfaceClass.getName());

+    }

+

+    public T getRef() {

+        return ref;

+    }

+

+    public void setRef(T ref) {

+        this.ref = ref;

+    }

+

+    @Parameter(excluded = true)

+    public String getPath() {

+        return path;

+    }

+

+    public void setPath(String path) {

+        checkPathName("path", path);

+        this.path = path;

+    }

+

+    public Boolean isRegister() {

+        return register;

+    }

+    

+    public void setRegister(Boolean register) {

+        this.register = register;

+        if (Boolean.FALSE.equals(register)) {

+            setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));

+        }

+    }

+    

+	public List<MethodConfig> getMethods() {

+		return methods;

+	}

+

+	@SuppressWarnings("unchecked")

+    public void setMethods(List<? extends MethodConfig> methods) {

+		this.methods = (List<MethodConfig>) methods;

+	}

+

+    public ProviderConfig getProvider() {

+        return provider;

+    }

+

+    public void setProvider(ProviderConfig provider) {

+        this.provider = provider;

+    }

+    

+    public List<URL> getExportedUrls(){

+        return urls;

+    }

+    

+    // ======== Deprecated ========

+

+    /**

+     * @deprecated Replace to getProtocols()

+     */

+    @Deprecated

+    public List<ProviderConfig> getProviders() {

+        return convertProtocolToProvider(protocols);

+    }

+

+    /**

+     * @deprecated Replace to setProtocols()

+     */

+    @Deprecated

+    public void setProviders(List<ProviderConfig> providers) {

+        this.protocols = convertProviderToProtocol(providers);

+    }

+

+    @Deprecated

+    private static final List<ProtocolConfig> convertProviderToProtocol(List<ProviderConfig> providers) {

+        if (providers == null || providers.size() == 0) {

+            return null;

+        }

+        List<ProtocolConfig> protocols = new ArrayList<ProtocolConfig>(providers.size());

+        for (ProviderConfig provider : providers) {

+            protocols.add(convertProviderToProtocol(provider));

+        }

+        return protocols;

+    }

+    

+    @Deprecated

+    private static final List<ProviderConfig> convertProtocolToProvider(List<ProtocolConfig> protocols) {

+        if (protocols == null || protocols.size() == 0) {

+            return null;

+        }

+        List<ProviderConfig> providers = new ArrayList<ProviderConfig>(protocols.size());

+        for (ProtocolConfig provider : protocols) {

+            providers.add(convertProtocolToProvider(provider));

+        }

+        return providers;

+    }

+    

+    @Deprecated

+    private static final ProtocolConfig convertProviderToProtocol(ProviderConfig provider) {

+        ProtocolConfig protocol = new ProtocolConfig();

+        protocol.setName(provider.getProtocol().getName());

+        protocol.setServer(provider.getServer());

+        protocol.setClient(provider.getClient());

+        protocol.setCodec(provider.getCodec());

+        protocol.setHost(provider.getHost());

+        protocol.setPort(provider.getPort());

+        protocol.setPath(provider.getPath());

+        protocol.setPayload(provider.getPayload());

+        protocol.setThreads(provider.getThreads());

+        protocol.setParameters(provider.getParameters());

+        return protocol;

+    }

+    

+    @Deprecated

+    private static final ProviderConfig convertProtocolToProvider(ProtocolConfig protocol) {

+        ProviderConfig provider = new ProviderConfig();

+        provider.setProtocol(protocol);

+        provider.setServer(protocol.getServer());

+        provider.setClient(protocol.getClient());

+        provider.setCodec(protocol.getCodec());

+        provider.setHost(protocol.getHost());

+        provider.setPort(protocol.getPort());

+        provider.setPath(protocol.getPath());

+        provider.setPayload(protocol.getPayload());

+        provider.setThreads(protocol.getThreads());

+        provider.setParameters(protocol.getParameters());

+        return provider;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.java
new file mode 100644
index 0000000..f1e78ef
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.java
@@ -0,0 +1,127 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.spring;
+
+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Map;

+

+import org.springframework.beans.factory.FactoryBean;

+import org.springframework.beans.factory.InitializingBean;

+import org.springframework.context.ApplicationContext;

+import org.springframework.context.ApplicationContextAware;

+

+import com.alibaba.dubbo.config.ApplicationConfig;

+import com.alibaba.dubbo.config.ConsumerConfig;

+import com.alibaba.dubbo.config.MonitorConfig;

+import com.alibaba.dubbo.config.Parameter;

+import com.alibaba.dubbo.config.ReferenceConfig;

+import com.alibaba.dubbo.config.RegistryConfig;

+import com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory;

+
+/**
+ * ReferenceFactoryBean
+ * 
+ * @author william.liangf
+ */
+public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean {
+
+	private static final long serialVersionUID = 213195494150089726L;
+	
+	private transient ApplicationContext applicationContext;
+
+	public void setApplicationContext(ApplicationContext applicationContext) {
+		this.applicationContext = applicationContext;

+		SpringExtensionFactory.addApplicationContext(applicationContext);
+	}
+    
+    public Object getObject() throws Exception {
+        return get();
+    }
+

+    public Class<?> getObjectType() {
+        return getInterfaceClass();
+    }
+

+    @Parameter(excluded = true)
+    public boolean isSingleton() {
+        return true;
+    }

+

+    @SuppressWarnings({ "unchecked"})

+    public void afterPropertiesSet() throws Exception {

+        if (getConsumer() == null) {

+            Map<String, ConsumerConfig> consumerConfigMap = applicationContext == null ? null  : applicationContext.getBeansOfType(ConsumerConfig.class, false, false);

+            if (consumerConfigMap != null && consumerConfigMap.size() > 0) {

+                ConsumerConfig consumerConfig = null;

+                Collection<ConsumerConfig> defaultConfigs = consumerConfigMap.values();

+                for (ConsumerConfig config : defaultConfigs) {

+                    if (config.getClass() == ConsumerConfig.class) {

+                        if (consumerConfig != null) {

+                            throw new IllegalStateException("Duplicate consumer configs: " + consumerConfig + " and " + config);

+                        }

+                        consumerConfig = config;

+                    }

+                }

+                if (consumerConfig != null) {

+                    setConsumer(consumerConfig);

+                }

+            }

+        }

+        if (getApplication() == null

+                && (getConsumer() == null || getConsumer().getApplication() == null)) {

+            Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(ApplicationConfig.class, false, false);

+            if (applicationConfigMap != null && applicationConfigMap.size() > 0) {

+                if (applicationConfigMap.size() > 1) {

+                    throw new IllegalStateException("Duplicate application configs: " + applicationConfigMap.values());

+                }

+                ApplicationConfig applicationConfig = applicationConfigMap.values().iterator().next();

+                setApplication(applicationConfig);

+            }

+        }

+        if ((getRegistries() == null || getRegistries().size() == 0)

+                && (getConsumer() == null || getConsumer().getRegistries() == null || getConsumer().getRegistries().size() == 0)

+                && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {

+            Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(RegistryConfig.class, false, false);

+            if (registryConfigMap != null && registryConfigMap.size() > 0) {

+                Collection<RegistryConfig> registryConfigs = registryConfigMap.values();

+                if (registryConfigs != null && registryConfigs.size() > 0) {

+                    super.setRegistries(new ArrayList<RegistryConfig>(registryConfigs));

+                }

+            }

+        }

+        if (getMonitor() == null

+                && (getConsumer() == null || getConsumer().getMonitor() == null)

+                && (getApplication() == null || getApplication().getMonitor() == null)) {

+            Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(MonitorConfig.class, false, false);

+            if (monitorConfigMap != null && monitorConfigMap.size() > 0) {

+                if (monitorConfigMap.size() > 1) {

+                    throw new IllegalStateException("Duplicate monitor configs: " + monitorConfigMap.values());

+                }

+                MonitorConfig monitorConfig = monitorConfigMap.values().iterator().next();

+                super.setMonitor(monitorConfig);

+            }

+        }

+        Boolean b = isInit();

+        if (b == null && getConsumer() != null) {

+            b = getConsumer().isInit();

+        }

+        if (b != null && b.booleanValue()) {

+            getObject();

+        }

+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java
new file mode 100644
index 0000000..9151551
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java
@@ -0,0 +1,176 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.spring;
+
+import java.lang.reflect.Method;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Map;

+

+import org.springframework.beans.factory.BeanNameAware;

+import org.springframework.beans.factory.InitializingBean;

+import org.springframework.context.ApplicationContext;

+import org.springframework.context.ApplicationContextAware;

+import org.springframework.context.ApplicationEvent;

+import org.springframework.context.ApplicationListener;

+import org.springframework.context.event.ContextRefreshedEvent;

+

+import com.alibaba.dubbo.config.ApplicationConfig;

+import com.alibaba.dubbo.config.MonitorConfig;

+import com.alibaba.dubbo.config.ProtocolConfig;

+import com.alibaba.dubbo.config.ProviderConfig;

+import com.alibaba.dubbo.config.RegistryConfig;

+import com.alibaba.dubbo.config.ServiceConfig;

+import com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory;

+
+/**
+ * ServiceFactoryBean
+ * 
+ * @author william.liangf
+ */
+public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, ApplicationContextAware, ApplicationListener, BeanNameAware {
+
+	private static final long serialVersionUID = 213195494150089726L;
+
+    private static transient ApplicationContext SPRING_CONTEXT;
+    
+	private transient ApplicationContext applicationContext;
+
+    private transient String beanName;
+
+    private transient boolean supportedApplicationListener;
+    

+	public static ApplicationContext getSpringContext() {
+	    return SPRING_CONTEXT;
+	}
+
+	public void setApplicationContext(ApplicationContext applicationContext) {
+		this.applicationContext = applicationContext;

+		SpringExtensionFactory.addApplicationContext(applicationContext);
+		if (applicationContext != null) {
+		    SPRING_CONTEXT = applicationContext;
+		    try {
+	            Method method = applicationContext.getClass().getMethod("addApplicationListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
+	            method.invoke(applicationContext, new Object[] {this});

+	            supportedApplicationListener = true;
+	        } catch (Throwable t) {
+	        }
+		}
+	}
+
+    public void setBeanName(String name) {
+        this.beanName = name;
+    }
+
+    public void onApplicationEvent(ApplicationEvent event) {
+        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
+            if (isDelay()) {
+                if (logger.isInfoEnabled()) {
+                    logger.info("The service ready on spring started. service: " + getInterface());
+                }
+                export();
+            }
+        }
+    }
+    
+    private boolean isDelay() {
+        Integer delay = getDelay();
+        ProviderConfig provider = getProvider();
+        if (delay == null && provider != null) {
+            delay = provider.getDelay();
+        }
+        return supportedApplicationListener && delay != null && delay.intValue() == -1;
+    }
+
+    @SuppressWarnings({ "unchecked" })
+	public void afterPropertiesSet() throws Exception {
+        if (getProvider() == null) {
+            Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null  : applicationContext.getBeansOfType(ProviderConfig.class, false, false);
+            if (providerConfigMap != null && providerConfigMap.size() > 0) {
+                Collection<ProviderConfig> providerConfigs = providerConfigMap.values();
+                ProviderConfig providerConfig = providerConfigs.iterator().next();
+                if (providerConfigs.size() > 1) {
+                    Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : applicationContext.getBeansOfType(ProtocolConfig.class, false, false);
+                    if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
+                        throw new IllegalStateException("Duplicate provider configs: " + providerConfigs);
+                    }
+                    for (ProviderConfig config : providerConfigs) {
+                        if (config.isDefault() != null && config.isDefault()) {
+                            providerConfig = config;
+                        }
+                    }
+                }
+                setProvider(providerConfig);
+            }
+        }
+        if (getApplication() == null
+                && (getProvider() == null || getProvider().getApplication() == null)) {
+            Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(ApplicationConfig.class, false, false);
+            if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
+                if (applicationConfigMap.size() > 1) {
+                    throw new IllegalStateException("Duplicate application configs: " + applicationConfigMap.values());
+                }
+                ApplicationConfig applicationConfig = applicationConfigMap.values().iterator().next();
+                setApplication(applicationConfig);
+            }
+        }
+        if ((getRegistries() == null || getRegistries().size() == 0)
+                && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)

+                && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
+            Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(RegistryConfig.class, false, false);
+            if (registryConfigMap != null && registryConfigMap.size() > 0) {
+                Collection<RegistryConfig> registryConfigs = registryConfigMap.values();
+                if (registryConfigs != null && registryConfigs.size() > 0) {
+                    super.setRegistries(new ArrayList<RegistryConfig>(registryConfigs));
+                }
+            }
+        }
+        if (getMonitor() == null
+                && (getProvider() == null || getProvider().getMonitor() == null)

+                && (getApplication() == null || getApplication().getMonitor() == null)) {
+            Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(MonitorConfig.class, false, false);
+            if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
+                if (monitorConfigMap.size() > 1) {
+                    throw new IllegalStateException("Duplicate monitor configs: " + monitorConfigMap.values());
+                }
+                MonitorConfig monitorConfig = monitorConfigMap.values().iterator().next();
+                super.setMonitor(monitorConfig);
+            }
+        }
+        if ((getProtocols() == null || getProtocols().size() == 0)
+                && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
+            Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : applicationContext.getBeansOfType(ProtocolConfig.class, false, false);
+            if (protocolConfigMap != null && protocolConfigMap.size() > 0) {

+                if (protocolConfigMap.size() > 1) {

+                    throw new IllegalStateException("Found multi-protocols: " + protocolConfigMap.values() + ", You must be set default protocol in: <dubbo:provider protocol=\"dubbo\" />, or set service protocol in: <dubbo:service protocol=\"dubbo\" />");

+                }
+                ProtocolConfig protocolConfig = protocolConfigMap.values().iterator().next();
+                setProtocol(protocolConfig);
+            }
+        }
+        if (getPath() == null || getPath().length() == 0) {
+            if (beanName != null && beanName.length() > 0 
+                    && getInterface() != null && getInterface().length() > 0
+                    && beanName.startsWith(getInterface())) {
+                setPath(beanName);
+            }
+        }

+        if (! isDelay()) {

+            export();

+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/extension/SpringExtensionFactory.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/extension/SpringExtensionFactory.java
new file mode 100644
index 0000000..a060a56
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/extension/SpringExtensionFactory.java
@@ -0,0 +1,55 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.spring.extension;

+

+import java.util.Set;

+

+import org.springframework.context.ApplicationContext;

+

+import com.alibaba.dubbo.common.extension.ExtensionFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+

+/**

+ * SpringExtensionFactory

+ * 

+ * @author william.liangf

+ */

+public class SpringExtensionFactory implements ExtensionFactory {

+    

+    private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>();

+    

+    public static void addApplicationContext(ApplicationContext context) {

+        contexts.add(context);

+    }

+

+    public static void removeApplicationContext(ApplicationContext context) {

+        contexts.remove(context);

+    }

+

+    @SuppressWarnings("unchecked")

+    public <T> T getExtension(Class<T> type, String name) {

+        for (ApplicationContext context : contexts) {

+            if (context.containsBean(name)) {

+                Object bean = context.getBean(name);

+                if (type.isInstance(bean)) {

+                    return (T) bean;

+                }

+            }

+        }

+        return null;

+    }

+

+}

diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
new file mode 100644
index 0000000..a420a87
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
@@ -0,0 +1,375 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.spring.schema;
+
+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.Date;

+import java.util.HashSet;

+import java.util.Set;

+import java.util.regex.Pattern;

+

+import org.springframework.beans.PropertyValue;

+import org.springframework.beans.factory.config.BeanDefinition;

+import org.springframework.beans.factory.config.BeanDefinitionHolder;

+import org.springframework.beans.factory.config.RuntimeBeanReference;

+import org.springframework.beans.factory.config.TypedStringValue;

+import org.springframework.beans.factory.support.ManagedList;

+import org.springframework.beans.factory.support.ManagedMap;

+import org.springframework.beans.factory.support.RootBeanDefinition;

+import org.springframework.beans.factory.xml.BeanDefinitionParser;

+import org.springframework.beans.factory.xml.ParserContext;

+import org.w3c.dom.Element;

+import org.w3c.dom.NamedNodeMap;

+import org.w3c.dom.Node;

+import org.w3c.dom.NodeList;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.config.ArgumentConfig;

+import com.alibaba.dubbo.config.MethodConfig;

+import com.alibaba.dubbo.config.MonitorConfig;

+import com.alibaba.dubbo.config.ProtocolConfig;

+import com.alibaba.dubbo.config.RegistryConfig;

+import com.alibaba.dubbo.rpc.Protocol;

+
+/**
+ * AbstractBeanDefinitionParser
+ * 
+ * @author william.liangf
+ */
+public class DubboBeanDefinitionParser implements BeanDefinitionParser {
+    
+    private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class);
+	
+    private final Class<?> beanClass;
+    
+    private final boolean required;
+
+    public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
+        this.beanClass = beanClass;
+        this.required = required;
+    }
+
+    public BeanDefinition parse(Element element, ParserContext parserContext) {
+        return parse(element, parserContext, beanClass, required);
+    }
+
+    @SuppressWarnings("unchecked")

+    private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition();
+        beanDefinition.setBeanClass(beanClass);
+        beanDefinition.setLazyInit(false);
+        String id = element.getAttribute("id");
+        if ((id == null || id.length() == 0) && required) {
+        	String generatedBeanName = element.getAttribute("name");
+        	if (generatedBeanName == null || generatedBeanName.length() == 0) {

+        	    if (ProtocolConfig.class.equals(beanClass)) {

+        	        generatedBeanName = "dubbo";

+        	    } else {
+        	        generatedBeanName = element.getAttribute("interface");

+        	    }

+        	}
+        	if (generatedBeanName == null || generatedBeanName.length() == 0) {
+        		generatedBeanName = beanClass.getName();
+        	}
+            id = generatedBeanName; 
+            int counter = 2;
+            while(parserContext.getRegistry().containsBeanDefinition(id)) {
+                id = generatedBeanName + (counter ++);
+            }
+        }
+        if (id != null && id.length() > 0) {
+            if (parserContext.getRegistry().containsBeanDefinition(id))  {
+        		throw new IllegalStateException("Duplicate spring bean id " + id);
+        	}
+            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);

+            beanDefinition.getPropertyValues().addPropertyValue("id", id);
+        }
+        if (ProtocolConfig.class.equals(beanClass)) {
+            for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
+                BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
+                PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
+                if (property != null) {
+                    Object value = property.getValue();
+                    if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
+                        definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
+                    }
+                }
+            }
+        }

+        Set<String> props = new HashSet<String>();

+        ManagedMap parameters = null;
+        for (Method setter : beanClass.getMethods()) {
+            String name = setter.getName();
+            if (name.length() > 3 && name.startsWith("set")
+                    && Modifier.isPublic(setter.getModifiers())
+                    && setter.getParameterTypes().length == 1) {
+                Class<?> type = setter.getParameterTypes()[0];
+                String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");

+                props.add(property);
+                Method getter = null;
+                try {
+                    getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
+                } catch (NoSuchMethodException e) {
+                    try {
+                        getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
+                    } catch (NoSuchMethodException e2) {
+                    }
+                }
+                if (getter == null 
+                        || ! Modifier.isPublic(getter.getModifiers())
+                        || ! type.equals(getter.getReturnType())) {
+                    continue;
+                }
+                if ("parameters".equals(property)) {
+                    parameters = parseParameters(element.getChildNodes(), beanDefinition);
+                } else if ("methods".equals(property)) {
+                    parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
+                } else if ("arguments".equals(property)) {
+                    parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
+                } else {

+                    String value = element.getAttribute(property);
+                    if (value != null) {
+                    	value = value.trim();
+                    	if (value.length() > 0) {
+                    		if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
+                            	RegistryConfig registryConfig = new RegistryConfig();
+                            	registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
+                            	beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
+                            } else if ("registry".equals(property) && value.indexOf(',') != -1) {
+                    			parseMultiRef("registries", value, beanDefinition, parserContext);
+                            } else if ("provider".equals(property) && value.indexOf(',') != -1) {
+                            	parseMultiRef("providers", value, beanDefinition, parserContext);
+                            } else if ("protocol".equals(property) && value.indexOf(',') != -1) {
+                                parseMultiRef("protocols", value, beanDefinition, parserContext);
+                            } else {
+                                Object reference;
+                                if (isPrimitive(type)) {
+                                    if ("async".equals(property) && "false".equals(value)
+                                            || "timeout".equals(property) && "0".equals(value)
+                                            || "delay".equals(property) && "0".equals(value)
+                                            || "version".equals(property) && "0.0.0".equals(value)
+                                            || "stat".equals(property) && "-1".equals(value)
+                                            || "reliable".equals(property) && "false".equals(value)) {
+                                        // 兼容旧版本xsd中的default值
+                                        value = null;
+                                    }
+                                    reference = value;
+                                } else if ("protocol".equals(property) 
+                                        && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)
+                                        && (! parserContext.getRegistry().containsBeanDefinition(value)
+                                                || ! ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
+                                    if ("dubbo:provider".equals(element.getTagName())) {
+                                        logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
+                                    }
+                                    // 兼容旧版本配置
+                                    ProtocolConfig protocol = new ProtocolConfig();
+                                    protocol.setName(value);
+                                    reference = protocol;
+                                } else if ("monitor".equals(property) 

+                                        && (! parserContext.getRegistry().containsBeanDefinition(value)

+                                                || ! MonitorConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {

+                                    // 兼容旧版本配置

+                                    reference = convertMonitor(value);

+                                } else if ("onreturn".equals(property)) {
+                                    int index = value.lastIndexOf(".");
+                                    String returnRef = value.substring(0, index);
+                                    String returnMethod = value.substring(index + 1);
+                                    reference = new RuntimeBeanReference(returnRef);
+                                    beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
+                                } else if ("onthrow".equals(property)) {
+                                    int index = value.lastIndexOf(".");
+                                    String throwRef = value.substring(0, index);
+                                    String throwMethod = value.substring(index + 1);
+                                    reference = new RuntimeBeanReference(throwRef);
+                                    beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);

+                                } else {
+                                    if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
+                                        BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
+                                        if (! refBean.isSingleton()) {
+                                            throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value+ "\" scope=\"singleton\" ...>");
+                                        }
+                                    }
+                                    reference = new RuntimeBeanReference(value);
+                                }
+		                        beanDefinition.getPropertyValues().addPropertyValue(property, reference);
+                            }
+                    	}
+                    }
+                }
+            }
+        }

+        NamedNodeMap attributes = element.getAttributes();

+        int len = attributes.getLength();

+        for (int i = 0; i < len; i++) {

+            Node node = attributes.item(i);

+            String name = node.getLocalName();

+            if (! props.contains(name)) {

+                if (parameters == null) {

+                    parameters = new ManagedMap();

+                }

+                String value = node.getNodeValue();

+                parameters.put(name, new TypedStringValue(value, String.class));

+            }

+        }

+        if (parameters != null) {

+            beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);

+        }
+        return beanDefinition;
+    }

+

+    private static final Pattern GROUP_AND_VERION = Pattern.compile("^[\\-.0-9_a-zA-Z]+(\\:[\\-.0-9_a-zA-Z]+)?$");

+    

+    protected static MonitorConfig convertMonitor(String monitor) {

+        if (monitor == null || monitor.length() == 0) {

+            return null;

+        }

+        if (GROUP_AND_VERION.matcher(monitor).matches()) {

+            String group;

+            String version;

+            int i = monitor.indexOf(':');

+            if (i > 0) {

+                group = monitor.substring(0, i);

+                version = monitor.substring(i + 1);

+            } else {

+                group = monitor;

+                version = null;

+            }

+            MonitorConfig monitorConfig = new MonitorConfig();

+            monitorConfig.setGroup(group);

+            monitorConfig.setVersion(version);

+            return monitorConfig;

+        }

+        return null;

+    }

+ 
+    private static boolean isPrimitive(Class<?> cls) {
+        return cls.isPrimitive() || cls == Boolean.class || cls == Byte.class
+                || cls == Character.class || cls == Short.class || cls == Integer.class
+                || cls == Long.class || cls == Float.class || cls == Double.class
+                || cls == String.class || cls == Date.class || cls == Class.class;
+    }
+    
+    @SuppressWarnings("unchecked")
+	private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition,
+            ParserContext parserContext) {
+    	String[] values = value.split("\\s*[,]+\\s*");
+		ManagedList list = null;
+        for (int i = 0; i < values.length; i++) {
+            String v = values[i];
+            if (v != null && v.length() > 0) {
+            	if (list == null) {
+                    list = new ManagedList();
+                }
+            	list.add(new RuntimeBeanReference(v));
+            }
+        }
+        beanDefinition.getPropertyValues().addPropertyValue(property, list);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition) {
+        if (nodeList != null && nodeList.getLength() > 0) {
+            ManagedMap parameters = null;
+            for (int i = 0; i < nodeList.getLength(); i++) {
+                Node node = nodeList.item(i);
+                if (node instanceof Element) {
+                    if ("parameter".equals(node.getNodeName())
+                            || "parameter".equals(node.getLocalName())) {
+                        if (parameters == null) {
+                            parameters = new ManagedMap();
+                        }
+                        String key = ((Element) node).getAttribute("key");
+                        String value = ((Element) node).getAttribute("value");
+                        boolean hide = "true".equals(((Element) node).getAttribute("hide"));
+                        if (hide) {
+                            key = Constants.HIDE_KEY_PREFIX + key;
+                        }
+                        parameters.put(key, new TypedStringValue(value, String.class));
+                    }
+                }
+            }

+            return parameters;
+        }

+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
+                              ParserContext parserContext) {
+        if (nodeList != null && nodeList.getLength() > 0) {
+            ManagedList methods = null;
+            for (int i = 0; i < nodeList.getLength(); i++) {
+                Node node = nodeList.item(i);
+                if (node instanceof Element) {
+                    Element element = (Element) node;
+                    if ("method".equals(node.getNodeName()) || "method".equals(node.getLocalName())) {
+                        String methodName = element.getAttribute("name");
+                        if (methodName == null || methodName.length() == 0) {
+                            throw new IllegalStateException("<dubbo:method> name attribute == null");
+                        }
+                        if (methods == null) {
+                            methods = new ManagedList();
+                        }
+                        BeanDefinition methodBeanDefinition = parse(((Element) node),
+                                parserContext, MethodConfig.class, false);
+                        String name = id + "." + methodName;
+                        BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(
+                                methodBeanDefinition, name);
+                        methods.add(methodBeanDefinitionHolder);
+                    }
+                }
+            }
+            if (methods != null) {
+                beanDefinition.getPropertyValues().addPropertyValue("methods", methods);
+            }
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    private static void parseArguments(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
+                              ParserContext parserContext) {
+        if (nodeList != null && nodeList.getLength() > 0) {
+            ManagedList arguments = null;
+            for (int i = 0; i < nodeList.getLength(); i++) {
+                Node node = nodeList.item(i);
+                if (node instanceof Element) {
+                    Element element = (Element) node;
+                    if ("argument".equals(node.getNodeName()) || "argument".equals(node.getLocalName())) {
+                        String argumentIndex = element.getAttribute("index");
+                        if (arguments == null) {
+                            arguments = new ManagedList();
+                        }
+                        BeanDefinition argumentBeanDefinition = parse(((Element) node),
+                                parserContext, ArgumentConfig.class, false);
+                        String name = id + "." + argumentIndex;
+                        BeanDefinitionHolder argumentBeanDefinitionHolder = new BeanDefinitionHolder(
+                                argumentBeanDefinition, name);
+                        arguments.add(argumentBeanDefinitionHolder);
+                    }
+                }
+            }
+            if (arguments != null) {
+                beanDefinition.getPropertyValues().addPropertyValue("arguments", arguments);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboNamespaceHandler.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboNamespaceHandler.java
new file mode 100644
index 0000000..c30e676
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboNamespaceHandler.java
@@ -0,0 +1,52 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.spring.schema;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ConsumerConfig;
+import com.alibaba.dubbo.config.MonitorConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ProviderConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.spring.ReferenceBean;
+import com.alibaba.dubbo.config.spring.ServiceBean;
+
+/**
+ * DubboNamespaceHandler
+ * 
+ * @author william.liangf
+ */
+public class DubboNamespaceHandler extends NamespaceHandlerSupport {
+
+	static {
+		Version.checkDuplicate(DubboNamespaceHandler.class);
+	}
+
+    public void init() {
+        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
+        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
+        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
+        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
+        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
+        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
+        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
+        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/DataSourceStatusChecker.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/DataSourceStatusChecker.java
new file mode 100644
index 0000000..4388d06
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/DataSourceStatusChecker.java
@@ -0,0 +1,91 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.spring.status;

+

+import java.sql.Connection;

+import java.sql.DatabaseMetaData;

+import java.sql.ResultSet;

+import java.util.Map;

+

+import javax.sql.DataSource;

+

+import org.springframework.context.ApplicationContext;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.config.spring.ServiceBean;

+

+/**

+ * DataSourceStatusChecker

+ * 

+ * @author william.liangf

+ */

+@Activate

+public class DataSourceStatusChecker implements StatusChecker {

+

+    private static final Logger logger = LoggerFactory.getLogger(DataSourceStatusChecker.class);

+

+    @SuppressWarnings("unchecked")

+    public Status check() {

+        ApplicationContext context = ServiceBean.getSpringContext();

+        if (context == null) {

+            return new Status(Status.Level.UNKNOWN);

+        }

+        Map<String, DataSource> dataSources = context.getBeansOfType(DataSource.class, false, false);

+        if (dataSources == null || dataSources.size() == 0) {

+            return new Status(Status.Level.UNKNOWN);

+        }

+        Status.Level level = Status.Level.OK;

+        StringBuilder buf = new StringBuilder();

+        for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {

+            DataSource dataSource = entry.getValue();

+            if (buf.length() > 0) {

+                buf.append(", ");

+            }

+            buf.append(entry.getKey());

+            try {

+                Connection connection = dataSource.getConnection();

+                try {

+                    DatabaseMetaData metaData = connection.getMetaData();

+                    ResultSet resultSet = metaData.getTypeInfo();

+                    try {

+                        if (! resultSet.next()) {

+                            level = Status.Level.ERROR;

+                        }

+                    } finally {

+                        resultSet.close();

+                    }

+                    buf.append(metaData.getURL());

+                    buf.append("(");

+                    buf.append(metaData.getDatabaseProductName());

+                    buf.append("-");

+                    buf.append(metaData.getDatabaseProductVersion());

+                    buf.append(")");

+                } finally {

+                    connection.close();

+                }

+            } catch (Throwable e) {

+                logger.warn(e.getMessage(), e);

+                return new Status(level, e.getMessage());

+            }

+        }

+        return new Status(level, buf.toString());

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/SpringStatusChecker.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/SpringStatusChecker.java
new file mode 100644
index 0000000..a2265b0
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/SpringStatusChecker.java
@@ -0,0 +1,86 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.spring.status;

+

+import java.lang.reflect.Method;

+

+import org.springframework.context.ApplicationContext;

+import org.springframework.context.Lifecycle;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.config.spring.ServiceBean;

+

+/**

+ * SpringStatusChecker

+ * 

+ * @author william.liangf

+ */

+@Activate

+public class SpringStatusChecker implements StatusChecker {

+    

+    private static final Logger logger = LoggerFactory.getLogger(SpringStatusChecker.class);

+

+    public Status check() {

+        ApplicationContext context = ServiceBean.getSpringContext();

+        if (context == null) {

+            return new Status(Status.Level.UNKNOWN);

+        }

+        Status.Level level = Status.Level.OK;

+        if (context instanceof Lifecycle) {

+            if (((Lifecycle)context).isRunning()) {

+                level = Status.Level.OK;

+            } else {

+                level = Status.Level.ERROR;

+            }

+        } else {

+            level = Status.Level.UNKNOWN;

+        }

+        StringBuilder buf = new StringBuilder();

+        try {

+            Class<?> cls = context.getClass();

+            Method method = null;

+            while (cls != null && method == null) {

+                try {

+                    method = cls.getDeclaredMethod("getConfigLocations", new Class<?>[0]);

+                } catch (NoSuchMethodException t) {

+                    cls = cls.getSuperclass();

+                }

+            }

+            if (method != null) {

+                if (! method.isAccessible()) {

+                    method.setAccessible(true);

+                }

+                String[] configs = (String[]) method.invoke(context, new Object[0]);

+                if (configs != null && configs.length > 0) {

+                    for (String config : configs) {

+                        if (buf.length() > 0) {

+                            buf.append(",");

+                        }

+                        buf.append(config);

+                    }

+                }

+            }

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+        return new Status(level, buf.toString());

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/src/main/resources/META-INF/dubbo.xsd
new file mode 100644
index 0000000..b2ef863
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/dubbo.xsd
@@ -0,0 +1,978 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>

+<xsd:schema xmlns="http://code.alibabatech.com/schema/dubbo"

+	xmlns:xsd="http://www.w3.org/2001/XMLSchema" 

+	targetNamespace="http://code.alibabatech.com/schema/dubbo">

+	

+	<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>

+	

+	<xsd:annotation>

+		<xsd:documentation><![CDATA[ Namespace support for the dubbo services provided by dubbo framework. ]]></xsd:documentation>

+	</xsd:annotation>

+	

+	<xsd:element name="application">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ The application config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:attribute name="id" type="xsd:ID">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="name" type="xsd:string" use="required">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The application name. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="owner" type="xsd:string">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The application owner name (email prefix). ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="organization" type="xsd:string">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The organization name. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="architecture" type="xsd:string">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The architecture. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="environment" type="xsd:string">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The application environment, eg: dev/test/run ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="compiler" type="xsd:string">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The java code compiler. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="registry" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The application registry. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="monitor" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The application monitor. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:element name="registry">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ The registry config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:sequence minOccurs="0" maxOccurs="unbounded">

+				<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />

+			</xsd:sequence>

+			<xsd:attribute name="id" type="xsd:ID">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="address" type="xsd:string" use="required">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The registry address. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="port" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The registry default port. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="protocol" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The registry lookup protocol. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="username" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The registry username. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="password" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The registry password. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="transport" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="transporter" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="server" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol server type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="client" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol client type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="group" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The registry group. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="version" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The registry version. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="timeout" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The request timeout. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="session" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The session timeout. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="file" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The registry adddress file store. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="wait" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The wait time for shutdown. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="check" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ Check registry status on stratup. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="dynamic" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ the service registered to this registry is dynamic(true) or static(false). ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="register" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ register service to this registry(true) or not(false). ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="subscribe" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ subscribe service to this registry(true) or not(false). ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:element name="monitor">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ The logstat monitor config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:sequence minOccurs="0" maxOccurs="unbounded">

+				<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />

+			</xsd:sequence>

+			<xsd:attribute name="address" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The monitor address. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="protocol" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The monitor protocol. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="username" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The monitor username. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="password" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The monitor password. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="group" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The monitor group. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="version" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The monitor version. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:element name="parameter">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ The service url parameter ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:attribute name="key" type="xsd:string" use="required">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The parameter key. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="value" type="xsd:string" use="required">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The parameter value. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="hide" type="xsd:boolean" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ Hide parameter. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:complexType name="methodShared">

+		<xsd:attribute name="timeout" type="xsd:string" use="optional" default="0">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ The method invoke timeout. ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+		<xsd:attribute name="retries" type="xsd:string" use="optional">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ The method retry times. ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+		<xsd:attribute name="actives" type="xsd:string" use="optional">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ The max active requests. ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+		<xsd:attribute name="connections" type="xsd:string" use="optional">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ The exclusive connections. default share one connection. ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+		<xsd:attribute name="loadbalance" type="xsd:string" use="optional">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ The method load balance. ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+		<xsd:attribute name="async" type="xsd:string" use="optional" default="false">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ The method does async. ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+		<xsd:attribute name="sent" type="xsd:string" use="optional">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ The async method return await message sent ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+		<xsd:attribute name="mock" type="xsd:string" use="optional">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ Use service mock implemention. ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+		<xsd:attribute name="validation" type="xsd:string" use="optional">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ Use service jsr303 validation, true/false. ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+		<xsd:attribute name="cache" type="xsd:string" use="optional">

+			<xsd:annotation>

+				<xsd:documentation><![CDATA[ Use service cache, lru/threadlocal/jcache. ]]></xsd:documentation>

+			</xsd:annotation>

+		</xsd:attribute>

+	</xsd:complexType>

+	

+	<xsd:element name="method">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ The service method config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:complexContent>

+				<xsd:extension base="methodShared">

+					<xsd:choice minOccurs="0" maxOccurs="unbounded">

+						<xsd:element ref="argument" minOccurs="0" maxOccurs="unbounded" />

+						<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />

+					</xsd:choice>

+					<xsd:attribute name="name" type="xsd:string" use="required">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The method name (method.toString()). ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="stat" type="xsd:string" use="optional" default="-1">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The method parameter index for statistics. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="retry" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Deprecated. Replace to retries. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="reliable" type="xsd:string" use="optional" default="false">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Deprecated. Replace to napoli protocol. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="deprecated" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The method deprecated. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="merger" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The multi-group result merger ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="sticky" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Enable/Disable cluster sticky policy.Default false ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="return" type="xsd:string" use="optional" >

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Method result is return. default is true.]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="oninvoke" type="xsd:string" use="optional" >

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Method invoke trigger.]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="onreturn" type="xsd:string" use="optional" >

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Method return trigger. return attribute must be true.]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="onthrow" type="xsd:string" use="optional" >

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Method on error trigger.return attribute must be true.]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+				</xsd:extension>

+			</xsd:complexContent>

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:element name="argument">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ The service argument config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:attribute name="index" type="xsd:string" use="optional" >

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The argument index. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="type" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The argument type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="callback" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The argument is callback. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:complexType name="referenceShared">

+		<xsd:complexContent>

+			<xsd:extension base="methodShared">

+				<xsd:attribute name="id" type="xsd:ID">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="local" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ Use service local implemention. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="stub" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ Use service local implemention. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="proxy" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ Use proxy factory. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="cluster" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ Use cluster strategy. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="filter" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The filter. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="listener" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The listener. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="owner" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The owner. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="layer" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ layer info. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="application" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service application. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="registry" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service registry. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="monitor" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service monitor. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="callbacks" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The callback instance limit peer connection.]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="onconnect" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service client connected. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="ondisconnect" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service client disconnected. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+			</xsd:extension>

+		</xsd:complexContent>

+	</xsd:complexType>

+	

+	<xsd:complexType name="consumerShared">

+		<xsd:complexContent>

+			<xsd:extension base="referenceShared">

+				<xsd:attribute name="check" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ Check dependency providers. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="init" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ Eager init reference. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="generic" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ Generic service. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="injvm" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ Get reference in jvm first. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="sticky" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Enable/Disable cluster sticky policy.Default false ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+				<xsd:attribute name="reconnect" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ remoting reconnect timer. false represent close reconnect. integer represent interval(ms) .default true(2000ms).]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="lazy" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ lazy create connection. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+			</xsd:extension>

+		</xsd:complexContent>

+	</xsd:complexType>

+	

+	<xsd:element name="consumer">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ Service reference default config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:complexContent>

+				<xsd:extension base="consumerShared">

+					<xsd:sequence minOccurs="0" maxOccurs="unbounded">

+						<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />

+					</xsd:sequence>

+					<xsd:anyAttribute namespace="##other" processContents="lax" />

+				</xsd:extension>

+			</xsd:complexContent>

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:element name="reference">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ Reference service config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:complexContent>

+				<xsd:extension base="consumerShared">

+					<xsd:choice minOccurs="0" maxOccurs="unbounded">

+						<xsd:element ref="method" minOccurs="0" maxOccurs="unbounded" />

+						<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />

+					</xsd:choice>

+					<xsd:attribute name="interface" type="xsd:token" use="required">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The service interface class name. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="version" type="xsd:string" use="optional" default="0.0.0">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The service version. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="group" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The service group. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="url" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Provider list url. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="client" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Protocol transport client type. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="consumer" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Deprecated. Replace to reference-default. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:anyAttribute namespace="##other" processContents="lax" />

+				</xsd:extension>

+			</xsd:complexContent>

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:element name="protocol">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ Service provider config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:sequence minOccurs="0" maxOccurs="unbounded">

+				<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />

+			</xsd:sequence>

+			<xsd:attribute name="id" type="xsd:ID">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="name" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol name. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="host" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The service host. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="port" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The service port. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="threadpool" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The thread pool type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="threads" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The thread pool size. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="iothreads" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The IO thread pool size. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="queues" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The thread pool queue size. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="accepts" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The accept connection size. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="codec" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol codec. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="serialization" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol serialization. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="charset" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol charset. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="payload" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The max payload. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="buffer" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The buffer size. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="heartbeat" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The heartbeat interval.(ms) ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="accesslog" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol use accesslog. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="telnet" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol use telnet commands. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="prompt" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol telnet prompt. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="status" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol check status. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="transporter" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="exchanger" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol exchanger type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="dispather" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol dispather type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="networker" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol "networker" type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="server" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol server type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="client" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol client type. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="path" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol context path. replace to "contextpath". ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="contextpath" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol context path. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:attribute name="register" type="xsd:string" use="optional">

+				<xsd:annotation>

+					<xsd:documentation><![CDATA[ The protocol can be register to registry. ]]></xsd:documentation>

+				</xsd:annotation>

+			</xsd:attribute>

+			<xsd:anyAttribute namespace="##other" processContents="lax" />

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:complexType name="serviceShared">

+		<xsd:complexContent>

+			<xsd:extension base="referenceShared">

+				<xsd:attribute name="version" type="xsd:string" use="optional" default="0.0.0">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service version. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="group" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service group. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="deprecated" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ whether the service is deprecated. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="delay" type="xsd:string" use="optional" default="0">

+	               	<xsd:annotation>

+	               		<xsd:documentation>

+	               			<![CDATA[ The service export delay millisecond. ]]>

+	               		</xsd:documentation>

+	               	</xsd:annotation>

+	            </xsd:attribute>

+	            <xsd:attribute name="weight" type="xsd:string" use="optional">

+	               	<xsd:annotation>

+	               		<xsd:documentation>

+	               			<![CDATA[ The service weight. ]]>

+	               		</xsd:documentation>

+	               	</xsd:annotation>

+	            </xsd:attribute>

+	            <xsd:attribute name="document" type="xsd:string" use="optional">

+	               	<xsd:annotation>

+	               		<xsd:documentation>

+	               			<![CDATA[ The service document. ]]>

+	               		</xsd:documentation>

+	               	</xsd:annotation>

+	            </xsd:attribute>

+				<xsd:attribute name="dynamic" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ the service registered to the registry is dynamic(true) or static(false). ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="token" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service use token. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="accesslog" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service use accesslog. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="executes" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service allow execute requests. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:attribute name="protocol" type="xsd:string" use="optional">

+					<xsd:annotation>

+						<xsd:documentation><![CDATA[ The service protocol. ]]></xsd:documentation>

+					</xsd:annotation>

+				</xsd:attribute>

+				<xsd:anyAttribute namespace="##other" processContents="lax" />

+			</xsd:extension>

+		</xsd:complexContent>

+	</xsd:complexType>

+

+	<xsd:element name="provider">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:complexContent>

+				<xsd:extension base="serviceShared">

+					<xsd:sequence minOccurs="0" maxOccurs="unbounded">

+						<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />

+					</xsd:sequence>

+					<xsd:attribute name="host" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The service host. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="port" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The service port. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="threadpool" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The thread pool type. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="threads" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The thread pool size. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="iothreads" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The IO thread pool size. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="queues" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The thread pool queue size. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="accepts" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The accept connection size. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="codec" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol codec. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="serialization" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol serialization. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="charset" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol charset. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="payload" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The max payload. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="buffer" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The buffer size. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="transporter" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="exchanger" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol exchanger type. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="dispather" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol dispather type. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="networker" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol "networker" type. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="server" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol server type. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="client" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol client type. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="telnet" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol use telnet commands. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="prompt" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol telnet prompt. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="status" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol check status. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="path" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol context path. replace to "contextpath". ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="contextpath" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The protocol context path. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="wait" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The provider shutdown wait time. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="default" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Deprecated. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:anyAttribute namespace="##other" processContents="lax" />

+				</xsd:extension>

+			</xsd:complexContent>

+		</xsd:complexType>

+	</xsd:element>

+	

+	<xsd:element name="service">

+		<xsd:annotation> 

+			<xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation> 

+		</xsd:annotation>

+		<xsd:complexType>

+			<xsd:complexContent>

+				<xsd:extension base="serviceShared">

+					<xsd:choice minOccurs="0" maxOccurs="unbounded">

+						<xsd:element ref="method" minOccurs="0" maxOccurs="unbounded" />

+						<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />

+					</xsd:choice>

+					<xsd:attribute name="ref" type="xsd:string" use="required">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Refers to the named bean to be exported as a service in the service registry. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="interface" type="xsd:token" use="required">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Defines the interface to advertise for this service in the service registry. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="path" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The service path. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="register" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ The service can be register to registry. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:attribute name="provider" type="xsd:string" use="optional">

+						<xsd:annotation>

+							<xsd:documentation><![CDATA[ Deprecated. Replace to protocol. ]]></xsd:documentation>

+						</xsd:annotation>

+					</xsd:attribute>

+					<xsd:anyAttribute namespace="##other" processContents="lax" />

+				</xsd:extension>

+			</xsd:complexContent>

+		</xsd:complexType>

+	</xsd:element>

+	

+</xsd:schema>
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory b/dubbo-config/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory
new file mode 100644
index 0000000..95935f0
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory
@@ -0,0 +1 @@
+spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-config/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..fa52ac2
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+spring=com.alibaba.dubbo.config.spring.status.SpringStatusChecker

+datasource=com.alibaba.dubbo.config.spring.status.DataSourceStatusChecker
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/META-INF/spring.handlers b/dubbo-config/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000..33510e6
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1 @@
+http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/META-INF/spring.schemas b/dubbo-config/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000..b520c36
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1 @@
+http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/ConfigTest.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/ConfigTest.java
new file mode 100644
index 0000000..7a613b9
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/ConfigTest.java
@@ -0,0 +1,685 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertNotNull;

+import static org.junit.Assert.assertNull;

+import static org.junit.Assert.assertThat;

+import static org.junit.Assert.assertTrue;

+import static org.junit.Assert.fail;

+import static org.junit.matchers.JUnitMatchers.containsString;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.config.spring.ServiceBean;

+import org.junit.Test;

+import org.springframework.beans.factory.BeanCreationException;

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.config.api.DemoService;

+import com.alibaba.dubbo.config.consumer.DemoActionByAnnotation;

+import com.alibaba.dubbo.config.consumer.DemoActionBySetter;

+import com.alibaba.dubbo.config.provider.impl.DemoServiceImpl;

+import com.alibaba.dubbo.config.support.MockFilter;

+import com.alibaba.dubbo.registry.RegistryService;

+import com.alibaba.dubbo.registry.support.SimpleRegistryExporter;

+import com.alibaba.dubbo.registry.support.SimpleRegistryService;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * ConfigTest

+ * 

+ * @author william.liangf

+ */

+public class ConfigTest {

+

+    @Test

+    public void testSpringExtensionInject() {

+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/spring-extension-inject.xml");

+        ctx.start();

+        try {

+            MockFilter filter = (MockFilter) ExtensionLoader.getExtensionLoader(Filter.class).getExtension("mymock");

+            assertNotNull(filter.getMockDao());

+            assertNotNull(filter.getProtocol());

+            assertNotNull(filter.getLoadBalance());

+        } finally {

+            ctx.stop();

+            ctx.close();

+        }

+    }

+

+    private DemoService refer(String url) {

+        ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();

+        reference.setApplication(new ApplicationConfig("consumer"));

+        reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));

+        reference.setInterface(DemoService.class);

+        reference.setUrl(url);

+        return reference.get();

+    }

+

+    @Test

+    public void testToString() {

+        ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();

+        reference.setApplication(new ApplicationConfig("consumer"));

+        reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));

+        reference.setInterface(DemoService.class);

+        reference.setUrl("dubbo://127.0.0.1:20881");

+        String str = reference.toString();

+        assertTrue(str.startsWith("<dubbo:reference "));

+        assertTrue(str.contains(" url=\"dubbo://127.0.0.1:20881\" "));

+        assertTrue(str.contains(" interface=\"com.alibaba.dubbo.config.api.DemoService\" "));

+        assertTrue(str.endsWith(" />"));

+    }

+    

+    @Test

+    public void testMultiProtocol() {

+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol.xml");

+        ctx.start();

+        try {

+            DemoService demoService = refer("dubbo://127.0.0.1:20881");

+            String hello = demoService.sayName("hello");

+            assertEquals("say:hello", hello);

+        } finally {

+            ctx.stop();

+            ctx.close();

+        }

+    }

+

+    @Test

+    public void testMultiProtocolDefault() {

+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-default.xml");

+        ctx.start();

+        try {

+            DemoService demoService = refer("rmi://127.0.0.1:10991");

+            String hello = demoService.sayName("hello");

+            assertEquals("say:hello", hello);

+        } finally {

+            ctx.stop();

+            ctx.close();

+        }

+    }

+    

+    @Test

+    public void testMultiProtocolError() {

+        try {

+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-error.xml");

+            ctx.start();

+            ctx.stop();

+            ctx.close();

+        } catch (BeanCreationException e) {

+            assertTrue(e.getMessage().contains("Found multi-protocols"));

+        }

+    }

+

+    @Test

+    public void testMultiProtocolRegister() {

+        SimpleRegistryService registryService = new SimpleRegistryService();

+        Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4547, registryService);

+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-register.xml");

+        ctx.start();

+        try {

+            List<URL> urls = registryService.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");

+            assertNotNull(urls);

+            assertEquals(1, urls.size());

+            assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20824/com.alibaba.dubbo.config.api.DemoService", urls.get(0).toIdentityString());

+        } finally {

+            ctx.stop();

+            ctx.close();

+            exporter.unexport();

+        }

+    }

+

+    @Test

+    public void testMultiRegistry() {

+        SimpleRegistryService registryService1 = new SimpleRegistryService();

+        Exporter<RegistryService> exporter1 = SimpleRegistryExporter.export(4545, registryService1);

+        SimpleRegistryService registryService2 = new SimpleRegistryService();

+        Exporter<RegistryService> exporter2 = SimpleRegistryExporter.export(4546, registryService2);

+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-registry.xml");

+        ctx.start();

+        try {

+            List<URL> urls1 = registryService1.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");

+            assertNull(urls1);

+            List<URL> urls2 = registryService2.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");

+            assertNotNull(urls2);

+            assertEquals(1, urls2.size());

+            assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20880/com.alibaba.dubbo.config.api.DemoService", urls2.get(0).toIdentityString());

+        } finally {

+            ctx.stop();

+            ctx.close();

+            exporter1.unexport();

+            exporter2.unexport();

+        }

+    }

+

+    @Test

+    public void testDelayFixedTime() throws Exception {

+        SimpleRegistryService registryService = new SimpleRegistryService();

+        Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4548, registryService);

+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/delay-fixed-time.xml");

+        ctx.start();

+        try {

+            List<URL> urls = registryService.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");

+            assertNull(urls);

+            while (urls == null) {

+                urls = registryService.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");

+                Thread.sleep(10);

+            }

+            assertNotNull(urls);

+            assertEquals(1, urls.size());

+            assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20883/com.alibaba.dubbo.config.api.DemoService", urls.get(0).toIdentityString());

+        } finally {

+            ctx.stop();

+            ctx.close();

+            exporter.unexport();

+        }

+    }

+

+    @Test

+    public void testDelayOnInitialized() throws Exception {

+        SimpleRegistryService registryService = new SimpleRegistryService();

+        Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4548, registryService);

+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/delay-on-initialized.xml");

+        //ctx.start();

+        try {

+            List<URL> urls = registryService.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");

+            assertNotNull(urls);

+            assertEquals(1, urls.size());

+            assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20883/com.alibaba.dubbo.config.api.DemoService", urls.get(0).toIdentityString());

+        } finally {

+            ctx.stop();

+            ctx.close();

+            exporter.unexport();

+        }

+    }

+    

+    @Test

+    public void testRmiTimeout() throws Exception {

+        if (System.getProperty("sun.rmi.transport.tcp.responseTimeout") != null) {

+            System.setProperty("sun.rmi.transport.tcp.responseTimeout", "");

+        }

+        ConsumerConfig consumer = new ConsumerConfig();

+        consumer.setTimeout(1000);

+        assertEquals("1000", System.getProperty("sun.rmi.transport.tcp.responseTimeout"));

+        consumer.setTimeout(2000);

+        assertEquals("1000", System.getProperty("sun.rmi.transport.tcp.responseTimeout"));

+    }

+

+    @Test

+    public void testAutowireAndAOP() throws Exception {

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider.xml");

+        providerContext.start();

+        try {

+            ClassPathXmlApplicationContext byNameContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/aop-autowire-byname.xml");

+            byNameContext.start();

+            try {

+                DemoActionBySetter demoActionBySetter = (DemoActionBySetter) byNameContext.getBean("demoActionBySetter");

+                assertNotNull(demoActionBySetter.getDemoService());

+                assertEquals("aop:say:hello", demoActionBySetter.getDemoService().sayName("hello"));

+                DemoActionByAnnotation demoActionByAnnotation = (DemoActionByAnnotation) byNameContext.getBean("demoActionByAnnotation");

+                assertNotNull(demoActionByAnnotation.getDemoService());

+                assertEquals("aop:say:hello", demoActionByAnnotation.getDemoService().sayName("hello"));

+            } finally {

+                byNameContext.stop();

+                byNameContext.close();

+            }

+            ClassPathXmlApplicationContext byTypeContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/aop-autowire-bytype.xml");

+            byTypeContext.start();

+            try {

+                DemoActionBySetter demoActionBySetter = (DemoActionBySetter) byTypeContext.getBean("demoActionBySetter");

+                assertNotNull(demoActionBySetter.getDemoService());

+                assertEquals("aop:say:hello", demoActionBySetter.getDemoService().sayName("hello"));

+                DemoActionByAnnotation demoActionByAnnotation = (DemoActionByAnnotation) byTypeContext.getBean("demoActionByAnnotation");

+                assertNotNull(demoActionByAnnotation.getDemoService());

+                assertEquals("aop:say:hello", demoActionByAnnotation.getDemoService().sayName("hello"));

+            } finally {

+                byTypeContext.stop();

+                byTypeContext.close();

+            }

+        } finally {

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+    

+    @Test

+    public void testAppendFilter() throws Exception {

+        ProviderConfig provider = new ProviderConfig();

+        provider.setFilter("classloader,monitor");

+        ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();

+        service.setFilter("accesslog,trace");

+        service.setProvider(provider);

+        service.setProtocol(new ProtocolConfig("dubbo", 20880));

+        service.setApplication(new ApplicationConfig("provider"));

+        service.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));

+        service.setInterface(DemoService.class);

+        service.setRef(new DemoServiceImpl());

+        try {

+            service.export();

+            List<URL> urls = service.toUrls();

+            assertNotNull(urls);

+            assertEquals(1, urls.size());

+            assertEquals("classloader,monitor,accesslog,trace", urls.get(0).getParameter("service.filter"));

+            

+            ConsumerConfig consumer = new ConsumerConfig();

+            consumer.setFilter("classloader,monitor");

+            ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();

+            reference.setFilter("accesslog,trace");

+            reference.setConsumer(consumer);

+            reference.setApplication(new ApplicationConfig("consumer"));

+            reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));

+            reference.setInterface(DemoService.class);

+            reference.setUrl("dubbo://" + NetUtils.getLocalHost() + ":20880?" + DemoService.class.getName() + "?check=false");

+            try {

+                reference.get();

+                urls = reference.toUrls();

+                assertNotNull(urls);

+                assertEquals(1, urls.size());

+                assertEquals("classloader,monitor,accesslog,trace", urls.get(0).getParameter("reference.filter"));

+            } finally {

+                reference.destroy();

+            }

+        } finally {

+            service.unexport();

+        }

+    }

+    

+    @Test

+    public void testInitReference() throws Exception {

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider.xml");

+        providerContext.start();

+        try {

+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/init-reference.xml");

+            ctx.start();

+            try {

+                DemoService demoService = (DemoService)ctx.getBean("demoService");

+                assertEquals("say:world", demoService.sayName("world"));

+            } finally {

+                ctx.stop();

+                ctx.close();

+            }

+        } finally {

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+    

+    // DUBBO-147   通过RpcContext可以获得所有尝试过的Invoker

+    @Test

+    public void test_RpcContext_getInvokers() throws Exception {

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(

+                ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider-long-waiting.xml");

+        providerContext.start();

+

+        try {

+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(

+                    ConfigTest.class.getPackage().getName().replace('.', '/')

+                            + "/init-reference-getInvokers.xml");

+            ctx.start();

+            try {

+                DemoService demoService = (DemoService) ctx.getBean("demoService");

+                try {

+                    demoService.sayName("Haha");

+                    fail();

+                } catch (RpcException expected) {

+                    assertThat(expected.getMessage(), containsString("Tried 3 times"));

+                }

+

+                assertEquals(3, RpcContext.getContext().getInvokers().size());

+            } finally {

+                ctx.stop();

+                ctx.close();

+            }

+        } finally {

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+    

+    // BUG: DUBBO-846 2.0.9中,服务方法上的retry="false"设置失效

+    @Test

+    public void test_retrySettingFail() throws Exception {

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(

+                ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider-long-waiting.xml");

+        providerContext.start();

+

+        try {

+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(

+                    ConfigTest.class.getPackage().getName().replace('.', '/')

+                            + "/init-reference-retry-false.xml");

+            ctx.start();

+            try {

+                DemoService demoService = (DemoService) ctx.getBean("demoService");

+                try {

+                    demoService.sayName("Haha");

+                    fail();

+                } catch (RpcException expected) {

+                    assertThat(expected.getMessage(), containsString("Tried 1 times"));

+                }

+

+                assertEquals(1, RpcContext.getContext().getInvokers().size());

+            } finally {

+                ctx.stop();

+                ctx.close();

+            }

+        } finally {

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+    

+    // BUG: DUBBO-146 Dubbo序列化失败(如传输对象没有实现Serialiable接口),Provider端也没有异常输出,Consumer端超时出错

+    @Test

+    public void test_returnSerializationFail() throws Exception {

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider-UnserializableBox.xml");

+        providerContext.start();

+        try {

+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/init-reference.xml");

+            ctx.start();

+            try {

+                DemoService demoService = (DemoService)ctx.getBean("demoService");

+                try {

+                    demoService.getBox();

+                    fail();

+                } catch (RpcException expected) {

+                    assertThat(expected.getMessage(), containsString("must implement java.io.Serializable"));

+                }

+            } finally {

+                ctx.stop();

+                ctx.close();

+            }

+        } finally {

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+

+    @Test

+    public void testXmlOverrideProperties() throws Exception {

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/xml-override-properties.xml");

+        providerContext.start();

+        try {

+            ApplicationConfig application = (ApplicationConfig) providerContext.getBean("application");

+            assertEquals("demo-provider", application.getName());

+            assertEquals("world", application.getOwner());

+            

+            RegistryConfig registry = (RegistryConfig) providerContext.getBean("registry");

+            assertEquals("N/A", registry.getAddress());

+            

+            ProtocolConfig dubbo = (ProtocolConfig) providerContext.getBean("dubbo");

+            assertEquals(20813, dubbo.getPort().intValue());

+            

+        } finally {

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+

+    @Test

+    public void testApiOverrideProperties() throws Exception {

+        ApplicationConfig application = new ApplicationConfig();

+        application.setName("api-override-properties");

+        

+        RegistryConfig registry = new RegistryConfig();

+        registry.setAddress("N/A");

+        

+        ProtocolConfig protocol = new ProtocolConfig();

+        protocol.setName("dubbo");

+        protocol.setPort(13123);

+        

+        ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();

+        service.setInterface(DemoService.class);

+        service.setRef(new DemoServiceImpl());

+        service.setApplication(application);

+        service.setRegistry(registry);

+        service.setProtocol(protocol);

+        service.export();

+        

+        try {

+            URL url = service.toUrls().get(0);

+            assertEquals("api-override-properties", url.getParameter("application"));

+            assertEquals("world", url.getParameter("owner"));

+            assertEquals(13123, url.getPort());

+            

+            ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();

+            reference.setApplication(new ApplicationConfig("consumer"));

+            reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));

+            reference.setInterface(DemoService.class);

+            reference.setUrl("dubbo://127.0.0.1:13123");

+            reference.get();

+            try {

+                url = reference.toUrls().get(0);

+                assertEquals("2000", url.getParameter("timeout"));

+            } finally {

+                reference.destroy();

+            }

+        } finally {

+            service.unexport();

+        }

+    }

+

+    @Test

+    public void testSystemPropertyOverrideProtocol() throws Exception {

+        System.setProperty("dubbo.protocol.port", "20812");

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/override-protocol.xml");

+        providerContext.start();

+        try {

+            ProtocolConfig dubbo = (ProtocolConfig) providerContext.getBean("dubbo");

+            assertEquals(20812, dubbo.getPort().intValue());

+        } finally {

+            System.setProperty("dubbo.protocol.port", "");

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+

+    @Test

+    public void testSystemPropertyOverrideMultiProtocol() throws Exception {

+        System.setProperty("dubbo.protocol.dubbo.port", "20814");

+        System.setProperty("dubbo.protocol.rmi.port", "10914");

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/override-multi-protocol.xml");

+        providerContext.start();

+        try {

+            ProtocolConfig dubbo = (ProtocolConfig) providerContext.getBean("dubbo");

+            assertEquals(20814, dubbo.getPort().intValue());

+            ProtocolConfig rmi = (ProtocolConfig) providerContext.getBean("rmi");

+            assertEquals(10914, rmi.getPort().intValue());

+        } finally {

+            System.setProperty("dubbo.protocol.dubbo.port", "");

+            System.setProperty("dubbo.protocol.rmi.port", "");

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+    

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testSystemPropertyOverrideXmlDefault() throws Exception {

+        System.setProperty("dubbo.application.name", "sysover");

+        System.setProperty("dubbo.application.owner", "sysowner");

+        System.setProperty("dubbo.registry.address", "N/A");

+        System.setProperty("dubbo.protocol.name", "dubbo");

+        System.setProperty("dubbo.protocol.port", "20819");

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/system-properties-override-default.xml");

+        providerContext.start();

+        try {

+            ServiceConfig<DemoService> service = (ServiceConfig<DemoService>) providerContext.getBean("demoServiceConfig");

+            assertEquals("sysover", service.getApplication().getName());

+            assertEquals("sysowner", service.getApplication().getOwner());

+            assertEquals("N/A", service.getRegistry().getAddress());

+            assertEquals("dubbo", service.getProtocol().getName());

+            assertEquals(20819, service.getProtocol().getPort().intValue());

+        } finally {

+            System.setProperty("dubbo.application.name", "");

+            System.setProperty("dubbo.application.owner", "");

+            System.setProperty("dubbo.registry.address", "");

+            System.setProperty("dubbo.protocol.name", "");

+            System.setProperty("dubbo.protocol.port", "");

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testSystemPropertyOverrideXml() throws Exception {

+        System.setProperty("dubbo.application.name", "sysover");

+        System.setProperty("dubbo.application.owner", "sysowner");

+        System.setProperty("dubbo.registry.address", "N/A");

+        System.setProperty("dubbo.protocol.name", "dubbo");

+        System.setProperty("dubbo.protocol.port", "20819");

+        ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/system-properties-override.xml");

+        providerContext.start();

+        try {

+            ServiceConfig<DemoService> service = (ServiceConfig<DemoService>) providerContext.getBean("demoServiceConfig");

+            URL url = service.toUrls().get(0);

+            assertEquals("sysover", url.getParameter("application"));

+            assertEquals("sysowner", url.getParameter("owner"));

+            assertEquals("dubbo", url.getProtocol());

+            assertEquals(20819, url.getPort());

+        } finally {

+            System.setProperty("dubbo.application.name", "");

+            System.setProperty("dubbo.application.owner", "");

+            System.setProperty("dubbo.registry.address", "");

+            System.setProperty("dubbo.protocol.name", "");

+            System.setProperty("dubbo.protocol.port", "");

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+

+    @Test

+    public void testSystemPropertyOverrideApiDefault() throws Exception {

+        System.setProperty("dubbo.application.name", "sysover");

+        System.setProperty("dubbo.application.owner", "sysowner");

+        System.setProperty("dubbo.registry.address", "N/A");

+        System.setProperty("dubbo.protocol.name", "dubbo");

+        System.setProperty("dubbo.protocol.port", "20834");

+        try {

+            ServiceConfig<DemoService> serviceConfig = new ServiceConfig<DemoService>();

+            serviceConfig.setInterface(DemoService.class);

+            serviceConfig.setRef(new DemoServiceImpl());

+            serviceConfig.export();

+            try {

+                assertEquals("sysover", serviceConfig.getApplication().getName());

+                assertEquals("sysowner", serviceConfig.getApplication().getOwner());

+                assertEquals("N/A", serviceConfig.getRegistry().getAddress());

+                assertEquals("dubbo", serviceConfig.getProtocol().getName());

+                assertEquals(20834, serviceConfig.getProtocol().getPort().intValue());

+            } finally {

+                serviceConfig.unexport();

+            }

+        } finally {

+            System.setProperty("dubbo.application.name", "");

+            System.setProperty("dubbo.application.owner", "");

+            System.setProperty("dubbo.registry.address", "");

+            System.setProperty("dubbo.protocol.name", "");

+            System.setProperty("dubbo.protocol.port", "");

+        }

+    }

+

+    @Test

+    public void testSystemPropertyOverrideApi() throws Exception {

+        System.setProperty("dubbo.application.name", "sysover");

+        System.setProperty("dubbo.application.owner", "sysowner");

+        System.setProperty("dubbo.registry.address", "N/A");

+        System.setProperty("dubbo.protocol.name", "dubbo");

+        System.setProperty("dubbo.protocol.port", "20834");

+        try {

+            ApplicationConfig application = new ApplicationConfig();

+            application.setName("aaa");

+            

+            RegistryConfig registry = new RegistryConfig();

+            registry.setAddress("127.0.0.1");

+            

+            ProtocolConfig protocol = new ProtocolConfig();

+            protocol.setName("rmi");

+            protocol.setPort(1099);

+            

+            ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();

+            service.setInterface(DemoService.class);

+            service.setRef(new DemoServiceImpl());

+            service.setApplication(application);

+            service.setRegistry(registry);

+            service.setProtocol(protocol);

+            service.export();

+            

+            try {

+                URL url = service.toUrls().get(0);

+                assertEquals("sysover", url.getParameter("application"));

+                assertEquals("sysowner", url.getParameter("owner"));

+                assertEquals("dubbo", url.getProtocol());

+                assertEquals(20834, url.getPort());

+            } finally {

+                service.unexport();

+            }

+        } finally {

+            System.setProperty("dubbo.application.name", "");

+            System.setProperty("dubbo.application.owner", "");

+            System.setProperty("dubbo.registry.address", "");

+            System.setProperty("dubbo.protocol.name", "");

+            System.setProperty("dubbo.protocol.port", "");

+        }

+    }

+

+    @Test

+    public void testSystemPropertyOverrideProperties() throws Exception {

+        String portString = System.getProperty( "dubbo.protocol.port");

+        System.clearProperty("dubbo.protocol.port");

+        try {

+            int port = 1234;

+            System.setProperty("dubbo.protocol.port", String.valueOf(port));

+            assertEquals("hello", ConfigUtils.getProperty("dubbo.application.name"));

+            assertEquals(port, Integer.parseInt(ConfigUtils.getProperty("dubbo.protocol.port")));

+        } finally {

+            if (portString != null) {

+                System.setProperty("dubbo.application.name", portString);

+            }

+        }

+    }

+

+    @Test

+    public void testCustomizeParameter() throws Exception {

+        ClassPathXmlApplicationContext context =

+                new ClassPathXmlApplicationContext("customize-parameter.xml");

+        context.start();

+        ServiceBean serviceBean = (ServiceBean) context.getBean("demoServiceExport");

+        URL url = (URL) serviceBean.toUrls().get(0);

+        assertEquals("protocol-paramA", url.getParameter("protocol.paramA"));

+        assertEquals("service-paramA", url.getParameter("service.paramA"));

+    }

+

+    @Test

+    public void testPath() throws Exception {

+        ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();

+        service.setPath("a/b$c");

+        try {

+            service.setPath("a?b");

+            fail();

+        } catch (IllegalStateException e) {

+            assertTrue(e.getMessage().contains(""));

+        }

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/GenericServiceTest.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/GenericServiceTest.java
new file mode 100644
index 0000000..5d39423
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/GenericServiceTest.java
@@ -0,0 +1,126 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+import org.junit.Assert;

+import org.junit.Test;

+

+import com.alibaba.dubbo.config.api.DemoException;

+import com.alibaba.dubbo.config.api.DemoService;

+import com.alibaba.dubbo.config.api.User;

+import com.alibaba.dubbo.config.provider.impl.DemoServiceImpl;

+import com.alibaba.dubbo.rpc.service.GenericException;

+import com.alibaba.dubbo.rpc.service.GenericService;

+

+/**

+ * GenericServiceTest

+ * 

+ * @author william.liangf

+ */

+public class GenericServiceTest {

+    

+    @Test

+    public void testGenericServiceException() {

+        ServiceConfig<GenericService> service = new ServiceConfig<GenericService>();

+        service.setApplication(new ApplicationConfig("generic-provider"));

+        service.setRegistry(new RegistryConfig("N/A"));

+        service.setProtocol(new ProtocolConfig("dubbo", 29581));

+        service.setInterface(DemoService.class.getName());

+        service.setRef(new GenericService() {

+            public Object $invoke(String method, String[] parameterTypes, Object[] args)

+                    throws GenericException {

+                if ("sayName".equals(method)) {

+                    return "Generic " + args[0];

+                }

+                if ("throwDemoException".equals(method)) {

+                    throw new GenericException(DemoException.class.getName(), "Generic");

+                }

+                if ("getUsers".equals(method)) {

+                    return args[0];

+                }

+                return null;

+            }

+        });

+        service.export();

+        try {

+            ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();

+            reference.setApplication(new ApplicationConfig("generic-consumer"));

+            reference.setInterface(DemoService.class);

+            reference.setUrl("dubbo://127.0.0.1:29581?generic=true");

+            DemoService demoService = reference.get();

+            try {

+                // say name

+                Assert.assertEquals("Generic Haha", demoService.sayName("Haha"));

+                // get users

+                List<User> users = new ArrayList<User>();

+                users.add(new User("Aaa"));

+                users = demoService.getUsers(users);

+                Assert.assertEquals("Aaa", users.get(0).getName());

+                // throw demo exception

+                try {

+                    demoService.throwDemoException();

+                    Assert.fail();

+                } catch (DemoException e) {

+                    Assert.assertEquals("Generic", e.getMessage());

+                }

+            } finally {

+                reference.destroy();

+            }

+        } finally {

+            service.unexport();

+        }

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testGenericReferenceException() {

+        ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();

+        service.setApplication(new ApplicationConfig("generic-provider"));

+        service.setRegistry(new RegistryConfig("N/A"));

+        service.setProtocol(new ProtocolConfig("dubbo", 29581));

+        service.setInterface(DemoService.class.getName());

+        service.setRef(new DemoServiceImpl());

+        service.export();

+        try {

+            ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();

+            reference.setApplication(new ApplicationConfig("generic-consumer"));

+            reference.setInterface(DemoService.class);

+            reference.setUrl("dubbo://127.0.0.1:29581");

+            reference.setGeneric(true);

+            GenericService genericService = reference.get();

+            try {

+                List<Map<String, Object>> users = new ArrayList<Map<String, Object>>();

+                Map<String, Object> user = new HashMap<String, Object>();

+                user.put("class", "com.alibaba.dubbo.config.api.User");

+                user.put("name", "actual.provider");

+                users.add(user);

+                users = (List<Map<String, Object>>) genericService.$invoke("getUsers", new String[] {List.class.getName()}, new Object[] {users});

+                Assert.assertEquals(1, users.size());

+                Assert.assertEquals("actual.provider", users.get(0).get("name"));

+            } finally {

+                reference.destroy();

+            }

+        } finally {

+            service.unexport();

+        }

+    }

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/Box.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/Box.java
new file mode 100644
index 0000000..d1f381d
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/Box.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.config.api;
+
+/**
+ * @author ding.lid
+ */
+public interface Box {
+    
+    String getName();
+    
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoException.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoException.java
new file mode 100644
index 0000000..d9ce796
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoException.java
@@ -0,0 +1,43 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.api;

+

+/**

+ * DemoException

+ * 

+ * @author william.liangf

+ */

+public class DemoException extends Exception {

+

+    private static final long serialVersionUID = -8213943026163641747L;

+

+    public DemoException() {

+        super();

+    }

+

+    public DemoException(String message, Throwable cause) {

+        super(message, cause);

+    }

+

+    public DemoException(String message) {

+        super(message);

+    }

+

+    public DemoException(Throwable cause) {

+        super(cause);

+    }

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoService.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoService.java
new file mode 100644
index 0000000..8615a26
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoService.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.api;
+

+import java.util.List;

+
+
+/**
+ * DemoService
+ * 
+ * @author william.liangf
+ */
+public interface DemoService {

+    
+    String sayName(String name);

+    

+    Box getBox();

+    

+    void throwDemoException() throws DemoException;

+    

+    List<User> getUsers(List<User> users);

+    
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/User.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/User.java
new file mode 100644
index 0000000..249a0ee
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/User.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.api;

+

+import java.io.Serializable;

+

+/**

+ * User

+ * 

+ * @author william.liangf

+ */

+public class User implements Serializable {

+    

+    private static final long serialVersionUID = 1L;

+    

+    private String name;

+

+    public User() {

+    }

+

+    public User(String name) {

+        this.name = name;

+    }

+

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        this.name = name;

+    }

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/cache/CacheService.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/cache/CacheService.java
new file mode 100644
index 0000000..5e5fe52
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/cache/CacheService.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.cache;

+

+/**

+ * ValidationService

+ * 

+ * @author william.liangf

+ */

+public interface CacheService {

+

+    String findCache(String id);

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/cache/CacheServiceImpl.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/cache/CacheServiceImpl.java
new file mode 100644
index 0000000..91d4a40
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/cache/CacheServiceImpl.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.cache;

+

+import java.util.concurrent.atomic.AtomicInteger;

+

+/**

+ * ValidationServiceImpl

+ * 

+ * @author william.liangf

+ */

+public class CacheServiceImpl implements CacheService {

+    

+    private final AtomicInteger i = new AtomicInteger();

+

+    public String findCache(String id) {

+        return "request: " + id + ", response: " + i.getAndIncrement();

+    }

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/cache/CacheTest.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/cache/CacheTest.java
new file mode 100644
index 0000000..ed917f6
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/cache/CacheTest.java
@@ -0,0 +1,82 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.cache;

+

+import junit.framework.TestCase;

+

+import org.junit.Assert;

+import org.junit.Test;

+

+import com.alibaba.dubbo.config.ApplicationConfig;

+import com.alibaba.dubbo.config.ProtocolConfig;

+import com.alibaba.dubbo.config.ReferenceConfig;

+import com.alibaba.dubbo.config.RegistryConfig;

+import com.alibaba.dubbo.config.ServiceConfig;

+

+/**

+ * CacheTest

+ * 

+ * @author william.liangf

+ */

+public class CacheTest extends TestCase {

+

+    @Test

+    public void testCache() throws Exception {

+        ServiceConfig<CacheService> service = new ServiceConfig<CacheService>();

+        service.setApplication(new ApplicationConfig("cache-provider"));

+        service.setRegistry(new RegistryConfig("N/A"));

+        service.setProtocol(new ProtocolConfig("dubbo", 29582));

+        service.setInterface(CacheService.class.getName());

+        service.setRef(new CacheServiceImpl());

+        service.export();

+        try {

+            ReferenceConfig<CacheService> reference = new ReferenceConfig<CacheService>();

+            reference.setApplication(new ApplicationConfig("cache-consumer"));

+            reference.setInterface(CacheService.class);

+            reference.setUrl("dubbo://127.0.0.1:29582?cache=true");

+            CacheService cacheService = reference.get();

+            try {

+                // 测试缓存生效,多次调用返回同样的结果。(服务器端自增长返回值)

+                String fix = null;

+                for (int i = 0; i < 3; i ++) {

+                    String result = cacheService.findCache("0");

+                    Assert.assertTrue(fix == null || fix.equals(result));

+                    fix = result;

+                    Thread.sleep(100);

+                }

+                

+                // LRU的缺省cache.size为1000,执行1001次,应有溢出

+                for (int n = 0; n < 1001; n ++) {

+                    String pre = null;

+                    for (int i = 0; i < 10; i ++) {

+                        String result = cacheService.findCache(String.valueOf(n));

+                        Assert.assertTrue(pre == null || pre.equals(result));

+                        pre = result;

+                    }

+                }

+                

+                // 测试LRU有移除最开始的一个缓存项

+                String result = cacheService.findCache("0");

+                Assert.assertFalse(fix == null || fix.equals(result));

+            } finally {

+                reference.destroy();

+            }

+        } finally {

+            service.unexport();

+        }

+    }

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionByAnnotation.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionByAnnotation.java
new file mode 100644
index 0000000..5fad4af
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionByAnnotation.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.consumer;

+

+import org.springframework.beans.factory.annotation.Autowired;

+

+import com.alibaba.dubbo.config.api.DemoService;

+

+/**

+ * DemoAction

+ * 

+ * @author william.liangf

+ */

+public class DemoActionByAnnotation {

+    

+    @Autowired

+    private DemoService demoService;

+    

+    public DemoService getDemoService() {

+        return demoService;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionBySetter.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionBySetter.java
new file mode 100644
index 0000000..a345bde
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionBySetter.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.consumer;

+

+import com.alibaba.dubbo.config.api.DemoService;

+

+/**

+ * DemoAction

+ * 

+ * @author william.liangf

+ */

+public class DemoActionBySetter {

+    

+    private DemoService demoService;

+

+    public DemoService getDemoService() {

+        return demoService;

+    }

+

+    public void setDemoService(DemoService demoService) {

+        this.demoService = demoService;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/consumer/DemoInterceptor.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/consumer/DemoInterceptor.java
new file mode 100644
index 0000000..e15a76e
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/consumer/DemoInterceptor.java
@@ -0,0 +1,32 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.consumer;

+

+import org.aopalliance.intercept.MethodInterceptor;

+import org.aopalliance.intercept.MethodInvocation;

+

+/**

+ * DemoInterceptor

+ * 

+ * @author william.liangf

+ */

+public class DemoInterceptor implements MethodInterceptor {

+    

+    public Object invoke(MethodInvocation invocation) throws Throwable {

+        return "aop:" + invocation.proceed();

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl.java
new file mode 100644
index 0000000..2d1dd9a
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.provider.impl;
+
+import java.util.List;

+

+import com.alibaba.dubbo.config.api.Box;

+import com.alibaba.dubbo.config.api.DemoException;

+import com.alibaba.dubbo.config.api.DemoService;

+import com.alibaba.dubbo.config.api.User;

+
+/**
+ * DemoServiceImpl
+ * 
+ * @author william.liangf
+ */
+public class DemoServiceImpl implements DemoService {

+    
+    public String sayName(String name) {
+        return "say:" + name;
+    }

+    

+    public Box getBox() {

+        return null;

+    }

+

+    public void throwDemoException() throws DemoException {

+        throw new DemoException("DemoServiceImpl");

+    }

+

+    public List<User> getUsers(List<User> users) {

+        return users;

+    }

+    
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl_LongWaiting.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl_LongWaiting.java
new file mode 100644
index 0000000..cb4a52b
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl_LongWaiting.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.config.provider.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.config.api.Box;
+import com.alibaba.dubbo.config.api.DemoException;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.api.User;
+
+/**
+ * DemoServiceImpl
+ * 
+ * @author william.liangf
+ */
+public class DemoServiceImpl_LongWaiting implements DemoService {
+    
+    public String sayName(String name) {
+        try {
+            Thread.sleep(100 * 1000);
+        } catch (InterruptedException e) {}
+        
+        return "say:" + name;
+    }
+    
+    public Box getBox() {
+        return null;
+    }
+
+    public void throwDemoException() throws DemoException {
+        throw new DemoException("LongWaiting");
+    }
+
+    public List<User> getUsers(List<User> users) {
+        return users;
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBox.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBox.java
new file mode 100644
index 0000000..ca45a69
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBox.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.config.provider.impl;
+
+import com.alibaba.dubbo.config.api.Box;
+
+/**
+ * @author ding.lid
+ */
+public class UnserializableBox implements Box {
+    private static final long serialVersionUID = -4141012025649711421L;
+    
+    private int    count = 3;
+    private String name  = "Jerry";
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return "Box [count=" + count + ", name=" + name + "]";
+    }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBoxDemoServiceImpl.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBoxDemoServiceImpl.java
new file mode 100644
index 0000000..118d934
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBoxDemoServiceImpl.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.config.provider.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.config.api.Box;
+import com.alibaba.dubbo.config.api.DemoException;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.api.User;
+
+/**
+ * DemoServiceImpl
+ * 
+ * @author william.liangf
+ */
+public class UnserializableBoxDemoServiceImpl implements DemoService {
+    
+    public String sayName(String name) {
+        return "say:" + name;
+    }
+    
+    public Box getBox() {
+        return new UnserializableBox();
+    }
+
+    public void throwDemoException() throws DemoException {
+        throw new DemoException("Unserializable");
+    }
+
+    public List<User> getUsers(List<User> users) {
+        return users;
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockDao.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockDao.java
new file mode 100644
index 0000000..2d3e75a
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockDao.java
@@ -0,0 +1,25 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.support;

+

+/**

+ * MockDao

+ * 

+ * @author william.liangf

+ */

+public interface MockDao {

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockDaoImpl.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockDaoImpl.java
new file mode 100644
index 0000000..978c14b
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockDaoImpl.java
@@ -0,0 +1,25 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.support;

+

+/**

+ * MockDaoImpl

+ * 

+ * @author william.liangf

+ */

+public class MockDaoImpl implements MockDao {

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockFilter.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockFilter.java
new file mode 100644
index 0000000..e41977f
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockFilter.java
@@ -0,0 +1,67 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.support;

+

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.LoadBalance;

+

+/**

+ * MockFilter

+ * 

+ * @author william.liangf

+ */

+public class MockFilter implements Filter {

+    

+    private LoadBalance loadBalance;

+

+    private Protocol protocol;

+    

+    private MockDao mockDao;

+

+    public MockDao getMockDao() {

+        return mockDao;

+    }

+

+    public void setMockDao(MockDao mockDao) {

+        this.mockDao = mockDao;

+    }

+

+    public LoadBalance getLoadBalance() {

+        return loadBalance;

+    }

+

+    public void setLoadBalance(LoadBalance loadBalance) {

+        this.loadBalance = loadBalance;

+    }

+

+    public Protocol getProtocol() {

+        return protocol;

+    }

+

+    public void setProtocol(Protocol protocol) {

+        this.protocol = protocol;

+    }

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        return invoker.invoke(invocation);

+    }

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.java
new file mode 100644
index 0000000..332556b
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.java
@@ -0,0 +1,82 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.support;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+
+/**
+ * 
+ * @author haomin.liuhm
+ *
+ */
+public class MockProtocol implements Protocol {
+
+    /* (non-Javadoc)
+     * @see com.alibaba.dubbo.rpc.Protocol#getDefaultPort()
+     */
+    public int getDefaultPort() {
+
+        return 0;
+    }
+
+    /* (non-Javadoc)
+     * @see com.alibaba.dubbo.rpc.Protocol#export(com.alibaba.dubbo.rpc.Invoker)
+     */
+    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see com.alibaba.dubbo.rpc.Protocol#refer(java.lang.Class, com.alibaba.dubbo.common.URL)
+     */
+    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+        
+        final URL u = url;
+        
+        return new Invoker<T>(){
+            public Class<T> getInterface(){
+                return null;
+            }
+            public URL getUrl(){
+                return u;
+            }
+            public boolean isAvailable(){
+                return true;
+            }
+            public Result invoke(Invocation invocation) throws RpcException{
+                return null;
+            }
+            
+            public void destroy(){
+                
+            }            
+        };
+    }
+
+    /* (non-Javadoc)
+     * @see com.alibaba.dubbo.rpc.Protocol#destroy()
+     */
+    public void destroy() {
+        
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistry.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistry.java
new file mode 100644
index 0000000..30446b0
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistry.java
@@ -0,0 +1,101 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.support;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+
+/**
+ * TODO Comment of MockRegistry
+ * @author haomin.liuhm
+ *
+ */
+public class MockRegistry implements Registry {
+
+    static URL subscribedUrl = new URL("null", "0.0.0.0", 0);
+    
+    public static URL getSubscribedUrl(){
+        return subscribedUrl;
+    }
+    
+    /* 
+     * @see com.alibaba.dubbo.common.Node#getUrl()
+     */
+    public URL getUrl() {
+        return null;
+    }
+
+    /* 
+     * @see com.alibaba.dubbo.common.Node#isAvailable()
+     */
+    public boolean isAvailable() {
+        return true;
+    }
+
+    /* 
+     * @see com.alibaba.dubbo.common.Node#destroy()
+     */
+    public void destroy() {
+        
+    }
+
+    /* 
+     * @see com.alibaba.dubbo.registry.RegistryService#register(com.alibaba.dubbo.common.URL)
+     */
+    public void register(URL url, NotifyListener listener) {
+        
+    }
+
+    /* 
+     * @see com.alibaba.dubbo.registry.RegistryService#unregister(com.alibaba.dubbo.common.URL)
+     */
+    public void unregister(URL url, NotifyListener listener) {
+        
+    }
+
+    /* 
+     * @see com.alibaba.dubbo.registry.RegistryService#subscribe(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.registry.NotifyListener)
+     */
+    public void subscribe(URL url, NotifyListener listener) {
+        this.subscribedUrl = url;
+        List<URL> urls = new ArrayList<URL>();
+        
+        urls.add(url.setProtocol("mockprotocol")
+                    .addParameter(Constants.METHODS_KEY, "sayHello"));
+        
+        listener.notify(urls);
+    }
+
+    /* 
+     * @see com.alibaba.dubbo.registry.RegistryService#unsubscribe(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.registry.NotifyListener)
+     */
+    public void unsubscribe(URL url, NotifyListener listener) {
+        
+    }
+
+    /* 
+     * @see com.alibaba.dubbo.registry.RegistryService#lookup(com.alibaba.dubbo.common.URL)
+     */
+    public List<URL> lookup(URL url) {
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.java
new file mode 100644
index 0000000..f3dedbf
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.support;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.RegistryFactory;

+
+/**
+ * TODO Comment of MockRegistryFactory
+ * @author haomin.liuhm
+ *
+ */
+public class MockRegistryFactory implements RegistryFactory {
+
+    /* 
+     * @see com.alibaba.dubbo.registry.RegistryFactory#getRegistry(com.alibaba.dubbo.common.URL)
+     */
+    public Registry getRegistry(URL url) {
+        
+        return new MockRegistry();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/RpcConfigGetSetProxy.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/RpcConfigGetSetProxy.java
new file mode 100644
index 0000000..9ddecaf
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/RpcConfigGetSetProxy.java
@@ -0,0 +1,169 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.support;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.AbstractConfig;
+
+
+/**
+ * @author haomin.liuhm
+ *
+ */
+public class RpcConfigGetSetProxy {
+
+    private static final String RPC_CONFIG_BASECLASS = AbstractConfig.class.getName();
+    private static final Logger log = LoggerFactory.getLogger(RpcConfigGetSetProxy.class);
+    
+    
+    private Object proxiee           = null;
+    private Class<?> proxieeClass   = null;
+    private Boolean isOk        = false;
+    
+    public RpcConfigGetSetProxy(Object p){
+        
+        if(p == null){
+            return;
+        }
+        
+        if (!isKindOf(p.getClass(), RPC_CONFIG_BASECLASS)){
+            return;
+        }
+        
+        proxiee = p;
+        //proxieeClass = c;
+        proxieeClass = p.getClass();
+        isOk = true;
+        
+    }
+    
+    public boolean isOk(){
+        return isOk;
+    }
+    
+    public Object setValue(String key, Object value){
+        
+        if (!isOk()){
+            return null;
+        }
+        
+        Method m = findSetMethod(key, value, proxieeClass);
+        return invoke(m, value);
+    }
+
+    public Object getValue(String key){
+        
+        if (!isOk()){
+            return null;
+        }
+        
+        Method m = findGetMethod(key, proxieeClass);
+        return invoke(m, null);
+    }
+
+    public static boolean isKindOf(Class<?> c, String type){
+
+        // get the class def for obj and type
+      
+        Class<?> tClass;
+        try {
+            tClass = Class.forName(type);
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+        
+        // check against type and superclasses
+        while ( c != null ) {
+            if ( c == tClass ) return true;
+            c = c.getSuperclass();
+        }
+        
+        return false;
+    }
+    
+    private Object invoke(Method m, Object value) {
+        
+        if (m == null){
+            return null;
+        }
+        
+        try {
+            if(value == null){
+                return m.invoke(proxiee, (Object[])null);
+            }else{
+                return m.invoke(proxiee, value);
+            }
+        } catch (IllegalArgumentException e) {
+            log.error("IllegalArgumentException", e);
+            return null;
+        } catch (IllegalAccessException e) {
+            log.error("IllegalAccessException", e);
+            return null;
+        } catch (InvocationTargetException e) {
+            log.error("InvocationTargetException", e);
+            return null;
+        }
+    }
+    
+    private Method findGetMethod(String key, Class<?> clazz){
+        
+        Method m = findMethod(key, null, "get", clazz);
+        if (m != null){
+            return m;
+        }
+        
+        return findMethod(key, null, "is", clazz);
+    }
+    
+    private Method findSetMethod(String key, Object value, Class<?> clazz){
+        
+        return findMethod(key, value, "set", clazz);
+    }
+    
+    private Method getMethod(String methodName, Object value, Class<?> clazz){
+        
+        try{
+            if (value == null){
+                return clazz.getMethod(methodName, (Class<?>[])null);
+            }else{
+                return clazz.getMethod(methodName, value.getClass());
+            }
+        }catch (SecurityException e) {
+            log.error("SecurityException: " + e.getMessage());
+            return null;
+        } catch (NoSuchMethodException e) {
+            log.error("NoSuchMethodException: " + e.getMessage());
+            return null;
+        }
+    }
+
+    private Method findMethod(String key, Object value, String prefix, Class<?> clazz){
+        
+        if(key.length() < 2){
+            return null;
+        }
+        
+        key = key.substring(0,1).toUpperCase() + key.substring(1);
+        String methodName = prefix + key;
+        
+        return getMethod(methodName, value, clazz);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/ExporterSideConfigUrlTest.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/ExporterSideConfigUrlTest.java
new file mode 100644
index 0000000..5df4864
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/ExporterSideConfigUrlTest.java
@@ -0,0 +1,110 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.url.test;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+
+/**
+ * @author haomin.liuhm
+ *
+ */
+public class ExporterSideConfigUrlTest extends UrlTestBase {
+    
+    private static final Logger log = LoggerFactory.getLogger(ExporterSideConfigUrlTest.class);
+    
+    // ======================================================
+    //   tests start
+    // ======================================================  
+    @BeforeClass
+    public static void start(){
+        
+        
+    }
+    
+    
+    @Before
+    public void setUp(){
+        
+        initServConf();
+        
+        return;
+    }
+    
+    @After()
+    public void teardown() {
+    }
+    
+    @Test
+    public void exporterMethodConfigUrlTest(){
+        
+        verifyExporterUrlGeneration(methodConfForService, methodConfForServiceTable);
+    }
+    
+    @Test
+    public void exporterServiceConfigUrlTest(){
+        
+        verifyExporterUrlGeneration(servConf, servConfTable);
+    }
+    
+    @Test
+    public void exporterProviderConfigUrlTest(){
+        
+        verifyExporterUrlGeneration(provConf, provConfTable);
+    }
+    
+    @Test
+    public void exporterRegistryConfigUrlTest(){
+        
+        //verifyExporterUrlGeneration(regConfForService, regConfForServiceTable);
+    }
+
+
+    protected <T> void verifyExporterUrlGeneration(T config, Object[][] dataTable) {
+        
+        // 1. fill corresponding config with data
+        ////////////////////////////////////////////////////////////
+        fillConfigs(config, dataTable, TESTVALUE1);
+        
+        // 2. export service and get url parameter string from db
+        ////////////////////////////////////////////////////////////
+        servConf.export();
+        String paramStringFromDb = getProviderParamString();
+        try {
+            paramStringFromDb = URLDecoder.decode(paramStringFromDb, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            // impossible
+        }
+        
+        
+        assertUrlStringWithLocalTable(paramStringFromDb, dataTable, config.getClass().getName(), TESTVALUE1);
+        
+       
+        // 4. unexport service
+        ////////////////////////////////////////////////////////////
+        servConf.unexport();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/InvokerSideConfigUrlTest.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/InvokerSideConfigUrlTest.java
new file mode 100644
index 0000000..cfd1f24
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/InvokerSideConfigUrlTest.java
@@ -0,0 +1,236 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.url.test;
+
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ConsumerConfig;
+import com.alibaba.dubbo.config.MethodConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.api.DemoService;

+import com.alibaba.dubbo.config.support.MockRegistry;

+ 
+
+/**
+ * @author haomin.liuhm
+ *
+ */
+public class InvokerSideConfigUrlTest extends UrlTestBase {
+    private static final Logger log = LoggerFactory.getLogger(InvokerSideConfigUrlTest.class);
+
+    // ======================================================
+    //   invoker related data preparing
+    // ======================================================  
+    private ApplicationConfig   appConfForConsumer;
+    private ApplicationConfig   appConfForReference;
+    private RegistryConfig      regConfForConsumer;
+    private RegistryConfig      regConfForReference;
+    private MethodConfig        methodConfForReference;
+    private ConsumerConfig      consumerConf;
+    private ReferenceConfig<DemoService>     refConf;
+    
+    private Object appConfForConsumerTable[][] = {
+            {"", "", "", "", "", "", "", "", "", ""}, 
+        }; 
+
+    private Object appConfForReferenceTable[][] = {
+            {"", "", "", "", "", "", "", "", "", ""}, 
+        }; 
+    
+    private Object regConfForConsumerTable[][] = {
+//            {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""}, 
+//            {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""}, 
+//            {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""}, 
+//            {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""}, 
+            {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""}, 
+            {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+        };
+    
+    private Object regConfForReferenceTable[][] = {
+            {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""}, 
+            {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""}, 
+            {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""}, 
+            {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""}, 
+            {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""}, 
+            {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+        };
+    
+    private Object methodConfForReferenceTable[][] = {
+            {"actives", "eatTiger.actives", "int", 0, 90, "", "", "", "", ""}, 
+            {"executes", "eatTiger.executes", "int", 0, 90, "", "", "", "", ""}, 
+            {"deprecated", "eatTiger.deprecated", "boolean", false, true, "", "", "", "", ""}, 
+            {"async", "eatTiger.async", "boolean", false, true, "", "", "", "", ""}, 
+            {"timeout", "eatTiger.timeout", "int", 0, 90, "", "", "", "", ""}, 
+        };
+    
+    private Object refConfTable[][] = {
+//            {"version", "version", "string", "0.0.0", "1.2.3", "", "", "", "", ""}, 
+//            {"group", "group", "string", "", "HaominTest", "", "", "", "", ""}, 
+            
+//            {"delay", "delay", "int", 0, 5, "", "", "", "", ""}, // not boolean 
+            {"timeout", "timeout", "int", 5000, 3000, "", "", "", "", ""}, 
+            {"retries", "retries", "int", 2, 5, "", "", "", "", ""}, 
+            {"connections", "connections", "boolean", 100, 20, "", "", "", "", ""}, 
+            {"loadbalance", "loadbalance", "string", "random", "roundrobin", "leastactive", "", "", ""}, 
+            {"async", "async", "boolean", false, true, "", "", "", "", ""}, 
+            //excluded = true
+//            {"generic", "generic", "boolean", false, true, "", "", "", "", ""},  
+            {"check", "check", "boolean", false, true, "", "", "", "", ""}, 
+            //{"local", "local", "string", "false", "HelloServiceLocal", "true", "", "", "", ""}, 
+            //{"local", "local", "string", "false", "true", "", "", "", "", ""}, 
+            //{"mock", "mock", "string", "false", "dubbo.test.HelloServiceMock", "true", "", "", "", ""}, 
+            {"mock", "mock", "string", "false", "false", "", "", "", "", ""}, 
+            {"proxy", "proxy", "boolean", "javassist", "jdk", "", "", "", "", ""}, 
+            {"client", "client", "string", "netty", "mina", "", "", "", "", ""}, 
+            {"client", "client", "string", "netty", "mina", "", "", "", "", ""}, 
+            {"owner", "owner", "string", "", "haomin/ludvik", "", "", "", "", ""}, 
+            {"actives", "actives", "int", 0, 30, "", "", "", "", ""},
+            {"cluster", "cluster", "string", "failover", "failfast", "failsafe", "failback", "forking", "", ""}, 
+            //excluded = true
+//            {"filter", "service.filter", "string", "default", "-generic", "", "", "", "", ""}, 
+            //excluded = true
+//            {"listener", "exporter.listener", "string", "default", "-deprecated", "", "", "", "", ""}, 
+            //{"", "", "", "", "", "", "", "", "", ""}, 
+        }; 
+    
+    private Object consumerConfTable[][] = {
+            {"timeout", "default.timeout", "int", 5000, 8000, "", "", "", "", ""}, 
+            {"retries", "default.retries", "int", 2, 5, "", "", "", "", ""}, 
+            {"loadbalance", "default.loadbalance", "string", "random", "leastactive", "", "", "", "", ""}, 
+            {"async", "default.async", "boolean", false, true, "", "", "", "", ""}, 
+            {"connections", "default.connections", "int", 100, 5, "", "", "", "", ""}, 
+//            {"generic", "generic", "boolean", false, false, "", "", "", "", ""}, 
+            {"check", "check", "boolean", true, false, "", "", "", "", ""}, 
+            {"proxy", "proxy", "string", "javassist", "jdk", "javassist", "", "", "", ""}, 
+            {"owner", "owner", "string", "", "haomin", "", "", "", "", ""}, 
+            {"actives", "default.actives", "int", 0, 5, "", "", "", "", ""}, 
+            {"cluster", "default.cluster", "string", "failover", "forking", "", "", "", "", ""}, 
+            {"filter", "", "string", "", "", "", "", "", "", ""}, 
+            {"listener", "", "string", "", "", "", "", "", "", ""}, 
+//            {"", "", "", "", "", "", "", "", "", ""}, 
+        }; 
+    
+    // ======================================================
+    //   test Start
+    // ====================================================== 
+    
+    @BeforeClass
+    public static void start(){
+        
+        //RegistryController.startRegistryIfAbsence(1);
+    }
+    
+    
+    @Before
+    public void setUp(){
+        
+        initServConf();
+        initRefConf();
+        
+        return;
+    }
+    
+    @After()
+    public void teardown() {
+
+        //RegistryServer.reloadCache();
+    }
+    
+    
+    @Test
+    public void consumerConfUrlTest(){
+        verifyInvokerUrlGeneration(consumerConf, consumerConfTable);
+    }
+    
+    @Test
+    public void refConfUrlTest(){
+        verifyInvokerUrlGeneration(refConf, refConfTable);
+    }
+    
+    @Ignore
+    @Test //注册中心的参数不会在与consumer的query merge
+    public void regConfForConsumerUrlTest(){
+        
+        verifyInvokerUrlGeneration(regConfForConsumer, regConfForConsumerTable);
+        
+    }
+    
+    // ======================================================
+    //   private helper
+    // ====================================================== 
+    private void initRefConf(){
+        
+        appConfForConsumer      = new ApplicationConfig();
+        appConfForReference     = new ApplicationConfig();
+        regConfForConsumer      = new RegistryConfig();
+        regConfForReference     = new RegistryConfig();
+        methodConfForReference  = new MethodConfig();
+        
+        refConf                 = new ReferenceConfig<DemoService>();
+        consumerConf            = new ConsumerConfig();
+

+        methodConfForReference.setName("sayName");

+        regConfForReference.setAddress("127.0.0.1:9090");

+        regConfForReference.setProtocol("mockregistry");

+        appConfForReference.setName("ConfigTests");

+        refConf.setInterface("com.alibaba.dubbo.config.api.DemoService");

+        
+        refConf.setApplication(appConfForReference);
+        consumerConf.setApplication(appConfForConsumer);
+        
+        refConf.setRegistry(regConfForReference);
+        consumerConf.setRegistry(regConfForConsumer);
+        
+        refConf.setConsumer(consumerConf);
+        
+        refConf.setMethods(Arrays.asList(new MethodConfig[]{methodConfForReference}));
+    }
+    
+    private <T> void verifyInvokerUrlGeneration(T config, Object[][] dataTable){
+        servConf.export();
+        
+        fillConfigs(config, dataTable, TESTVALUE1);
+        refConf.get();
+        
+        String subScribedUrlStr = getSubscribedUrlString();
+        
+        System.out.println("url string=========:"+subScribedUrlStr);
+        String configName = config.getClass().getName();
+        int column = TESTVALUE1;
+        
+        assertUrlStringWithLocalTable(subScribedUrlStr, dataTable, configName, column);
+        
+        //重新refer会判断如果已经订阅过,不再重新订阅。
+        try {
+            refConf.destroy();
+        } catch (Exception e) {
+        }
+    }
+
+    private String getSubscribedUrlString() {
+        return MockRegistry.getSubscribedUrl().toString();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/UrlTestBase.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/UrlTestBase.java
new file mode 100644
index 0000000..85ec90d
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/UrlTestBase.java
@@ -0,0 +1,218 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.config.url.test;
+
+
+import java.util.Arrays;
+
+import org.junit.Assert;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.MethodConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ProviderConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.ServiceConfig;
+import com.alibaba.dubbo.config.api.DemoService;

+import com.alibaba.dubbo.config.provider.impl.DemoServiceImpl;

+import com.alibaba.dubbo.config.support.RpcConfigGetSetProxy;

+
+/**
+ * @author haomin.liuhm
+ *
+ */
+
+@SuppressWarnings("unused")
+public class UrlTestBase {
+    
+    private static final Logger log = LoggerFactory.getLogger(UrlTestBase.class);
+
+    // ======================================================
+    //   data column definition
+    // ====================================================== 
+    protected static final int KEY = 0;
+    protected static final int URL_KEY = 1;
+    private static final int TYPE = 2;
+    private static final int DEFAULT = 3;
+    protected static final int TESTVALUE1 = 4;
+    private static final int TESTVALUE2 = 5;
+    private static final int TESTVALUE3 = 6;
+    private static final int TESTVALUE4 = 7;
+    private static final int TESTVALUE5 = 8;
+    private static final int TESTVALUE6 = 9;
+    private static final int TESTVALUE7 = 10;
+    protected ApplicationConfig appConfForProvider;
+    protected ApplicationConfig appConfForService;
+    protected RegistryConfig regConfForProvider;
+    protected RegistryConfig regConfForService;
+    protected ProviderConfig provConf;
+    protected ProtocolConfig protoConfForProvider;
+    protected ProtocolConfig protoConfForService;
+    protected MethodConfig methodConfForService;
+    protected ServiceConfig<DemoService> servConf;
+    protected Object servConfTable[][] = {
+            {"proxy", "proxy", "string", "javassist", "jdk", "javassist", "", "", "", ""}, 
+            {"actives", "actives", "int", 0, 90, "", "", "", "", ""}, 
+            {"executes", "executes", "int", 0, 90, "", "", "", "", ""}, 
+            {"deprecated", "deprecated", "boolean", false, true, "", "", "", "", ""}, 
+            {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+            {"accesslog", "accesslog", "string", "", "haominTest", "", "", "", "", ""}, 
+            {"document", "document", "string", "", "http://b2b-doc.alibaba-inc.com/display/RC/dubbo_devguide.htm?testquery=你好你好", "", "", "", "", ""},
+            {"weight", "weight", "int", 0, 90, "", "", "", "", ""},
+            
+            //{"filter", "service.filter", "string", "", "", "", "", "", "", ""},
+            //{"listener", "listener", "string", "", "", "", "", "", "", ""},
+            
+            };
+
+    private Object appConfForProviderTable[][] = {
+                {"", "", "", "", "", "", "", "", "", ""}, 
+            };
+    private Object appConfForServiceTable[][] = {
+                {"", "", "", "", "", "", "", "", "", ""}, 
+            };
+    private Object regConfForProviderTable[][] = {
+                {"", "", "", "", "", "", "", "", "", ""}, 
+            };
+    protected Object regConfForServiceTable[][] = {
+    //            {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""}, 
+    //            {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""}, 
+    //            {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""}, 
+    //            {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""}, 
+    //            {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""}, 
+                {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+            };
+    private Object protoConfForProviderTable[][] = {
+                {"", "", "", "", "", "", "", "", "", ""}, 
+            };
+    private Object protoConfForServiceTable[][] = {
+                {"", "", "", "", "", "", "", "", "", ""}, 
+            };
+    protected Object provConfTable[][] = {
+                {"cluster", "default.cluster", "string", "string", "failover", "failfast", "failsafe", "", "", ""}, 
+                {"async", "default.async", "boolean", false, true, "", "", "", "", ""},
+                {"loadbalance", "default.loadbalance", "string", "random", "leastactive", "", "", "", "", ""},
+                {"connections", "default.connections", "int", 0, 60, "", "", "", "", ""},
+                {"retries", "default.retries", "int", 2, 60, "", "", "", "", ""},
+                {"timeout", "default.timeout", "int", 5000, 60, "", "", "", "", ""},
+                //change by fengting listener 没有缺省值
+                //{"listener", "exporter.listener", "string", "", "", "", "", "", "", ""},
+                //{"filter", "service.filter", "string", "", "", "", "", "", "", ""},
+                           
+            };
+    protected Object methodConfForServiceTable[][] = {
+                {"actives", "sayName.actives", "int", 0, 90, "", "", "", "", ""}, 
+                {"executes", "sayName.executes", "int", 0, 90, "", "", "", "", ""}, 
+                {"deprecated", "sayName.deprecated", "boolean", false, true, "", "", "", "", ""}, 
+                {"async", "sayName.async", "boolean", false, true, "", "", "", "", ""}, 
+                {"timeout", "sayName.timeout", "int", 0, 90, "", "", "", "", ""}, 
+            };
+    protected DemoService demoService = new DemoServiceImpl();
+    
+    // ======================================================
+    //   data table manipulation utils
+    // ====================================================== 
+    protected String genParamString(Object urlKey, Object value) {
+        
+        return (String)urlKey + "=" + value.toString();
+    }
+
+    protected <T> void fillConfigs(T conf, Object[][] table, int column) {
+        
+        for(Object[] row : table){
+            fillConfig(conf, row, column);
+        }
+    }
+
+    protected <T> void fillConfig(T conf, Object[] row, int column) {
+        
+        RpcConfigGetSetProxy proxy = new RpcConfigGetSetProxy(conf);
+        proxy.setValue((String)row[KEY], row[column]);
+        
+    }
+
+    @SuppressWarnings("deprecation")
+    protected void initServConf() {
+        
+        appConfForProvider = new ApplicationConfig();
+        appConfForService = new ApplicationConfig();
+        regConfForProvider = new RegistryConfig();
+        regConfForService = new RegistryConfig();
+        provConf = new ProviderConfig();
+        protoConfForProvider = new ProtocolConfig();
+        protoConfForService = new ProtocolConfig();
+        methodConfForService = new MethodConfig();
+        servConf = new ServiceConfig<DemoService>();
+        
+        provConf.setApplication(appConfForProvider);
+        servConf.setApplication(appConfForService);
+        
+        provConf.setRegistry(regConfForProvider);
+        servConf.setRegistry(regConfForService);
+        
+        provConf.setProtocols(Arrays.asList(new ProtocolConfig[]{protoConfForProvider}));
+        servConf.setProtocols(Arrays.asList(new ProtocolConfig[]{protoConfForService}));
+        
+        servConf.setMethods(Arrays.asList(new MethodConfig[]{methodConfForService}));
+        servConf.setProvider(provConf);
+        
+        servConf.setRef(demoService);
+        servConf.setInterfaceClass(DemoService.class);
+       
+        methodConfForService.setName("sayName");
+        regConfForService.setAddress("127.0.0.1:9090");
+        regConfForService.setProtocol("mockregistry");
+        appConfForService.setName("ConfigTests");
+    }
+
+    protected String getProviderParamString() {
+        return servConf.getExportedUrls().get(0).toString(); 
+    }
+
+    /**
+     * @param paramStringFromDb
+     * @param dataTable
+     * @param configName
+     * @param column
+     */
+    protected void assertUrlStringWithLocalTable(String paramStringFromDb, 
+                                                 Object[][] dataTable, String configName, int column) {
+        final String FAILLOG_HEADER = "The following config items are not found in URL: ";
+        
+        log.warn("Verifying service url for " + configName + "... ");
+        log.warn("Consumer url string: " + paramStringFromDb);
+        
+        String failLog = FAILLOG_HEADER;
+        for(Object[] row : dataTable){
+            
+            String targetString = genParamString(row[URL_KEY], row[column]);
+            
+            log.warn("Checking " + (String)row[KEY] + "for" + targetString);
+            if (paramStringFromDb.contains(targetString)){
+                log.warn((String)row[KEY] + " --> " + targetString + " OK!");
+            } else {
+                failLog += targetString + ", ";
+            }
+        }
+                                                        
+        if( !failLog.equals(FAILLOG_HEADER)){
+            Assert.fail(failLog);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationParameter.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationParameter.java
new file mode 100644
index 0000000..9ee41f6
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationParameter.java
@@ -0,0 +1,96 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.validation;

+

+import java.io.Serializable;

+import java.util.Date;

+

+import javax.validation.constraints.Future;

+import javax.validation.constraints.Max;

+import javax.validation.constraints.Min;

+import javax.validation.constraints.NotNull;

+import javax.validation.constraints.Past;

+import javax.validation.constraints.Pattern;

+import javax.validation.constraints.Size;

+

+/**

+ * ValidationParameter

+ * 

+ * @author william.liangf

+ */

+public class ValidationParameter implements Serializable {

+    

+    private static final long serialVersionUID = 7158911668568000392L;

+

+    @NotNull // 不允许为空

+    @Size(min = 1, max = 20) // 长度或大小范围

+    private String name;

+

+    @NotNull(groups = ValidationService.Save.class) // 保存时不允许为空,更新时允许为空 ,表示不更新该字段

+    @Pattern(regexp = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$")

+    private String email;

+

+    @Min(18) // 最小值

+    @Max(100) // 最大值

+    private int age;

+

+    @Past // 必须为一个过去的时间

+    private Date loginDate;

+

+    @Future // 必须为一个未来的时间

+    private Date expiryDate;

+

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        this.name = name;

+    }

+

+    public String getEmail() {

+        return email;

+    }

+

+    public void setEmail(String email) {

+        this.email = email;

+    }

+

+    public int getAge() {

+        return age;

+    }

+

+    public void setAge(int age) {

+        this.age = age;

+    }

+

+    public Date getLoginDate() {

+        return loginDate;

+    }

+

+    public void setLoginDate(Date loginDate) {

+        this.loginDate = loginDate;

+    }

+

+    public Date getExpiryDate() {

+        return expiryDate;

+    }

+

+    public void setExpiryDate(Date expiryDate) {

+        this.expiryDate = expiryDate;

+    }

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationService.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationService.java
new file mode 100644
index 0000000..36114d4
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationService.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.validation;

+

+import javax.validation.constraints.Min;

+

+

+/**

+ * ValidationService

+ * 

+ * @author william.liangf

+ */

+public interface ValidationService { // 缺省可按服务接口区分验证场景,如:@NotNull(groups = ValidationService.class)

+    

+    @interface Save{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Save.class),可选

+    void save(ValidationParameter parameter);

+

+    @interface Update{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Update.class),可选

+    void update(ValidationParameter parameter);

+    

+    void delete(@Min(1) long id);

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationServiceImpl.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationServiceImpl.java
new file mode 100644
index 0000000..552d1f7
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationServiceImpl.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.validation;

+

+/**

+ * ValidationServiceImpl

+ * 

+ * @author william.liangf

+ */

+public class ValidationServiceImpl implements ValidationService {

+

+    public void save(ValidationParameter parameter) {

+    }

+

+    public void update(ValidationParameter parameter) {

+    }

+

+    public void delete(long id) {

+    }

+

+}

diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationTest.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationTest.java
new file mode 100644
index 0000000..31a9e25
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/validation/ValidationTest.java
@@ -0,0 +1,98 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.config.validation;

+

+import java.util.Date;

+import java.util.Set;

+

+import javax.validation.ConstraintViolation;

+import javax.validation.ConstraintViolationException;

+

+import org.junit.Assert;

+import org.junit.Test;

+

+import com.alibaba.dubbo.config.ApplicationConfig;

+import com.alibaba.dubbo.config.ProtocolConfig;

+import com.alibaba.dubbo.config.ReferenceConfig;

+import com.alibaba.dubbo.config.RegistryConfig;

+import com.alibaba.dubbo.config.ServiceConfig;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * GenericServiceTest

+ * 

+ * @author william.liangf

+ */

+public class ValidationTest {

+

+    @Test

+    public void testValidation() {

+        ServiceConfig<ValidationService> service = new ServiceConfig<ValidationService>();

+        service.setApplication(new ApplicationConfig("validation-provider"));

+        service.setRegistry(new RegistryConfig("N/A"));

+        service.setProtocol(new ProtocolConfig("dubbo", 29582));

+        service.setInterface(ValidationService.class.getName());

+        service.setRef(new ValidationServiceImpl());

+        service.export();

+        try {

+            ReferenceConfig<ValidationService> reference = new ReferenceConfig<ValidationService>();

+            reference.setApplication(new ApplicationConfig("validation-consumer"));

+            reference.setInterface(ValidationService.class);

+            reference.setUrl("dubbo://127.0.0.1:29582?validation=true");

+            ValidationService validationService = reference.get();

+            try {

+                // Save OK

+                ValidationParameter parameter = new ValidationParameter();

+                parameter.setName("liangfei");

+                parameter.setEmail("liangfei@liang.fei");

+                parameter.setAge(50);

+                parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));

+                parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));

+                validationService.save(parameter);

+                

+                // Save Error

+                try {

+                    parameter = new ValidationParameter();

+                    validationService.save(parameter);

+                    Assert.fail();

+                } catch (RpcException e) {

+                    e.printStackTrace();

+                    ConstraintViolationException ve = (ConstraintViolationException)e.getCause();

+                    Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();

+                    Assert.assertNotNull(violations);

+                }

+                

+                // Delete OK

+                validationService.delete(2);

+                

+                // Delete Error

+                try {

+                    validationService.delete(0);

+                    Assert.fail();

+                } catch (RpcException e) {

+                    ConstraintViolationException ve = (ConstraintViolationException)e.getCause();

+                    Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();

+                    Assert.assertNotNull(violations);

+                }

+            } finally {

+                reference.destroy();

+            }

+        } finally {

+            service.unexport();

+        }

+    }

+

+}

diff --git a/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..6dd0bbd
--- /dev/null
+++ b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+mockregistry=com.alibaba.dubbo.config.support.MockRegistryFactory

diff --git a/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..3116623
--- /dev/null
+++ b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+mymock=com.alibaba.dubbo.config.support.MockFilter
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..b95dc2e
--- /dev/null
+++ b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+mockprotocol=com.alibaba.dubbo.config.support.MockProtocol
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/aop-autowire-byname.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/aop-autowire-byname.xml
new file mode 100644
index 0000000..4e152ac
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/aop-autowire-byname.xml
@@ -0,0 +1,60 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:context="http://www.springframework.org/schema/context"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd" default-autowire="byName">

+    

+    <context:annotation-config />

+    <bean id="demoInterceptor" class="com.alibaba.dubbo.config.consumer.DemoInterceptor" />

+	<bean id="demoAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

+        <property name="advice" ref="demoInterceptor" />

+        <property name="patterns">

+            <list>

+                <value>.*</value>

+            </list>

+        </property>

+    </bean>

+    <bean id="demoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

+        <property name="beanNames">

+            <list>

+                <value>demoService</value>

+            </list>

+        </property>

+        <property name="interceptorNames">

+            <list>

+                <value>demoAdvisor</value>

+            </list>

+        </property>

+    </bean>

+

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="consumer" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="N/A" />

+    

+    <!-- 引用服务配置 -->

+    <dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.api.DemoService" url="dubbo://127.0.0.1:20813" />

+     

+	<bean id="demoActionBySetter" class="com.alibaba.dubbo.config.consumer.DemoActionBySetter" />

+ 

+	<bean id="demoActionByAnnotation" class="com.alibaba.dubbo.config.consumer.DemoActionByAnnotation" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/aop-autowire-bytype.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/aop-autowire-bytype.xml
new file mode 100644
index 0000000..ff5c1cb
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/aop-autowire-bytype.xml
@@ -0,0 +1,60 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:context="http://www.springframework.org/schema/context"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd" default-autowire="byName">

+    

+    <context:annotation-config />

+    <bean id="demoInterceptor" class="com.alibaba.dubbo.config.consumer.DemoInterceptor" />

+	<bean id="demoAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

+        <property name="advice" ref="demoInterceptor" />

+        <property name="patterns">

+            <list>

+                <value>.*</value>

+            </list>

+        </property>

+    </bean>

+    <bean id="demoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

+        <property name="beanNames">

+            <list>

+                <value>demoService</value>

+            </list>

+        </property>

+        <property name="interceptorNames">

+            <list>

+                <value>demoAdvisor</value>

+            </list>

+        </property>

+    </bean>

+    

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="consumer" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="N/A" />

+    

+    <!-- 引用服务配置 -->

+    <dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.api.DemoService" url="dubbo://127.0.0.1:20813" />

+

+	<bean id="demoActionBySetter" class="com.alibaba.dubbo.config.consumer.DemoActionBySetter" />

+ 

+	<bean id="demoActionByAnnotation" class="com.alibaba.dubbo.config.consumer.DemoActionByAnnotation" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-fixed-time.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-fixed-time.xml
new file mode 100644
index 0000000..62d22b4
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-fixed-time.xml
@@ -0,0 +1,36 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="127.0.0.1:4548" />

+    

+    <dubbo:protocol name="dubbo" port="20883" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" delay="300" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-on-initialized.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-on-initialized.xml
new file mode 100644
index 0000000..0ad0f4e
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-on-initialized.xml
@@ -0,0 +1,36 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="127.0.0.1:4548" />

+    

+    <dubbo:protocol name="dubbo" port="20883" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" delay="-1" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/demo-provider-UnserializableBox.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/demo-provider-UnserializableBox.xml
new file mode 100644
index 0000000..bd2d1df
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/demo-provider-UnserializableBox.xml
@@ -0,0 +1,37 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -  
+ - 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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+    ">
+     
+    <!-- 当前应用信息配置 -->
+    <dubbo:application name="demo-provider" />
+    
+    <!-- 连接注册中心配置 -->
+    <dubbo:registry address="N/A" />
+     
+    <!-- 暴露服务协议配置 -->
+    <dubbo:protocol name="dubbo" port="20813" />
+    
+    <!-- 暴露服务配置 -->
+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />
+     
+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.UnserializableBoxDemoServiceImpl" />
+ 
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/demo-provider-long-waiting.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/demo-provider-long-waiting.xml
new file mode 100644
index 0000000..1d78904
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/demo-provider-long-waiting.xml
@@ -0,0 +1,38 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -  
+ - 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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+    ">
+     
+    <!-- 当前应用信息配置 -->
+    <dubbo:application name="demo-provider" />
+    
+    <!-- 连接注册中心配置 -->
+    <dubbo:registry address="N/A" />
+     
+    <!-- 暴露服务协议配置 -->
+    <dubbo:protocol id="dubbo1" name="dubbo" port="20813" />
+    <dubbo:protocol id="dubbo2" name="dubbo" port="20814" />
+    
+    <!-- 暴露服务配置 -->
+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" protocol="dubbo1,dubbo2" />
+     
+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl_LongWaiting" />
+ 
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/demo-provider.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/demo-provider.xml
new file mode 100644
index 0000000..7691738
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/demo-provider.xml
@@ -0,0 +1,37 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="N/A" />

+     

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol name="dubbo" port="20813" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/init-reference-getInvokers.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/init-reference-getInvokers.xml
new file mode 100644
index 0000000..2e5a4ec
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/init-reference-getInvokers.xml
@@ -0,0 +1,31 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -  
+ - 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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+    ">
+     
+    <!-- 当前应用信息配置 -->
+    <dubbo:application name="demo-consumer" />
+    
+    <!-- 暴露服务配置 -->
+    <dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.api.DemoService" url="dubbo://127.0.0.1:20813;dubbo://127.0.0.1:20814" init="true" timeout="100" >
+    	<dubbo:parameter key="connec.timeout" value="1000"/> 
+    </dubbo:reference>
+     
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/init-reference-retry-false.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/init-reference-retry-false.xml
new file mode 100644
index 0000000..2d5dafc
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/init-reference-retry-false.xml
@@ -0,0 +1,32 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -  
+ - 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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+    ">
+     
+    <!-- 当前应用信息配置 -->
+    <dubbo:application name="demo-consumer" />
+    
+    <!-- 暴露服务配置 -->
+    <dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.api.DemoService" url="dubbo://127.0.0.1:20813;dubbo://127.0.0.1:20814" init="true" timeout="100" >
+    	<dubbo:parameter key="connec.timeout" value="1000"/> 
+    	<dubbo:method name="sayName" retry="false" />
+    </dubbo:reference>
+     
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/init-reference.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/init-reference.xml
new file mode 100644
index 0000000..22679e2
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/init-reference.xml
@@ -0,0 +1,29 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-consumer" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.api.DemoService" url="dubbo://127.0.0.1:20813" init="true" />

+     

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-default.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-default.xml
new file mode 100644
index 0000000..85ae98c
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-default.xml
@@ -0,0 +1,41 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="N/A" />

+     

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol name="dubbo" port="20881" />

+    

+    <dubbo:protocol name="rmi" port="10991" />

+    

+    <dubbo:provider protocol="rmi" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-error.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-error.xml
new file mode 100644
index 0000000..474750a
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-error.xml
@@ -0,0 +1,39 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="N/A" />

+     

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol name="dubbo" port="20881" />

+    

+    <dubbo:protocol name="rmi" port="10991" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-register.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-register.xml
new file mode 100644
index 0000000..4be9bde
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-register.xml
@@ -0,0 +1,39 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="127.0.0.1:4547" />

+    

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol name="dubbo" port="20824" />

+    

+    <dubbo:protocol name="rmi" port="10924" register="false" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" protocol="dubbo,rmi" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol.xml
new file mode 100644
index 0000000..5b2638c
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol.xml
@@ -0,0 +1,39 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="N/A" />

+     

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol name="dubbo" port="20881" />

+    

+    <dubbo:protocol name="rmi" port="10991" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" protocol="dubbo" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-registry.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-registry.xml
new file mode 100644
index 0000000..f9231b4
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-registry.xml
@@ -0,0 +1,39 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry id="reg1" address="127.0.0.1:4545" />

+    

+    <dubbo:registry id="reg2" address="127.0.0.1:4546" />

+     

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol name="dubbo" port="20880" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" registry="reg2" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/override-multi-protocol.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/override-multi-protocol.xml
new file mode 100644
index 0000000..3754739
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/override-multi-protocol.xml
@@ -0,0 +1,40 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="N/A" />

+     

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol name="dubbo" port="20813" />

+    <dubbo:protocol name="rmi" port="10913" />

+    

+    <dubbo:provider protocol="dubbo,rmi" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/override-protocol.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/override-protocol.xml
new file mode 100644
index 0000000..28b1d54
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/override-protocol.xml
@@ -0,0 +1,37 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+     

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="N/A" />

+     

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol port="20813" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/spring-extension-inject.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/spring-extension-inject.xml
new file mode 100644
index 0000000..fa568c0
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/spring-extension-inject.xml
@@ -0,0 +1,36 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="N/A" />

+     

+    <bean id="mockDao" class="com.alibaba.dubbo.config.support.MockDaoImpl" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service id="demoServiceConfig" interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" filter="mymock" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/system-properties-override-default.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/system-properties-override-default.xml
new file mode 100644
index 0000000..ed573f5
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/system-properties-override-default.xml
@@ -0,0 +1,28 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+

+    <!-- 暴露服务配置 -->

+    <dubbo:service id="demoServiceConfig" interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/system-properties-override.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/system-properties-override.xml
new file mode 100644
index 0000000..d3899a7
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/system-properties-override.xml
@@ -0,0 +1,37 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+

+    <!-- 当前应用信息配置 -->

+    <dubbo:application name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry address="127.0.0.1" />

+     

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol name="rmi" port="20813" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service id="demoServiceConfig" interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/xml-override-properties.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/xml-override-properties.xml
new file mode 100644
index 0000000..de32dea
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/xml-override-properties.xml
@@ -0,0 +1,37 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd

+    ">

+

+    <!-- 当前应用信息配置 -->

+    <dubbo:application id="application" name="demo-provider" />

+    

+    <!-- 连接注册中心配置 -->

+    <dubbo:registry id="registry" address="N/A" />

+     

+    <!-- 暴露服务协议配置 -->

+    <dubbo:protocol name="dubbo" port="20813" />

+    

+    <!-- 暴露服务配置 -->

+    <dubbo:service id="demoServiceConfig" interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />

+     

+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />

+ 

+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/customize-parameter.xml b/dubbo-config/src/test/resources/customize-parameter.xml
new file mode 100644
index 0000000..dd22d3b
--- /dev/null
+++ b/dubbo-config/src/test/resources/customize-parameter.xml
@@ -0,0 +1,35 @@
+<!--
+  - Copyright 1999-2012 Alibaba Group.
+  -
+  -   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.
+  -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:context="http://www.springframework.org/schema/context"
+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+    xmlns:p="http://www.springframework.org/schema/p"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd" default-autowire="byName">
+
+    <dubbo:application name="customize-parameter" />
+
+    <dubbo:registry address="N/A" id="naRegistry" />
+
+    <dubbo:protocol p:protocol-paramA="protocol-paramA" />
+
+    <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />
+    
+    <dubbo:service id="demoServiceExport" p:service-paramA="service-paramA" registry="naRegistry" ref="demoService" interface="com.alibaba.dubbo.config.api.DemoService" />
+
+</beans>
diff --git a/dubbo-config/src/test/resources/dubbo.properties b/dubbo-config/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..98b869b
--- /dev/null
+++ b/dubbo-config/src/test/resources/dubbo.properties
@@ -0,0 +1,5 @@
+dubbo.application.name=hello

+dubbo.application.owner=world

+dubbo.registry.address=10.20.153.17

+dubbo.protocol.port=20881

+dubbo.service.invoke.timeout=2000
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/log4j.xml b/dubbo-config/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-config/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">

+	<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

+		<layout class="org.apache.log4j.PatternLayout">

+			<param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />

+		</layout>

+	</appender>

+	<root>

+		<level value="INFO" />

+		<appender-ref ref="CONSOLE" />

+	</root>

+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-container/pom.xml b/dubbo-container/pom.xml
new file mode 100644
index 0000000..3c96b64
--- /dev/null
+++ b/dubbo-container/pom.xml
@@ -0,0 +1,62 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+	<modelVersion>4.0.0</modelVersion>

+	<parent>

+		<groupId>com.alibaba</groupId>

+		<artifactId>dubbo-parent</artifactId>

+		<version>2.1.1</version>

+	</parent>

+	<artifactId>dubbo-container</artifactId>

+	<packaging>jar</packaging>

+	<name>Dubbo Container Module</name>

+	<description>The container module of dubbo project</description>

+	<properties>

+		<skip_maven_deploy>true</skip_maven_deploy>

+	</properties>

+	<dependencies>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-common</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>org.mortbay.jetty</groupId>

+			<artifactId>jetty</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>org.springframework</groupId>

+			<artifactId>spring</artifactId>

+		</dependency>

+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<groupId>org.apache.maven.plugins</groupId>

+				<artifactId>maven-jar-plugin</artifactId>

+				<configuration>

+					<archive>

+						<addMavenDescriptor>true</addMavenDescriptor>

+						<manifest>

+							<mainClass>com.alibaba.dubbo.container.Main</mainClass>

+						</manifest>

+					</archive>

+				</configuration>

+			</plugin>

+		</plugins>

+	</build>

+</project>
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/Container.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/Container.java
new file mode 100644
index 0000000..d65c3c6
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/Container.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container;

+

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * Container. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@SPI("spring")

+public interface Container {

+    

+    /**

+     * start.

+     */

+    void start();

+    

+    /**

+     * stop.

+     */

+    void stop();

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/Main.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/Main.java
new file mode 100644
index 0000000..3a76713
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/Main.java
@@ -0,0 +1,95 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container;

+

+import java.text.SimpleDateFormat;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.Date;

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+

+/**

+ * Main. (API, Static, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class Main {

+

+    public static final String CONTAINER_KEY = "dubbo.container";

+    

+    private static final Logger logger = LoggerFactory.getLogger(Main.class);

+

+    private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);

+    

+    private static volatile boolean running = true;

+

+    public static void main(String[] args) {

+        try {

+            if (args == null || args.length == 0) {

+                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());

+                args = Constants.COMMA_SPLIT_PATTERN.split(config);

+            }

+            

+            final List<Container> containers = new ArrayList<Container>();

+            for (int i = 0; i < args.length; i ++) {

+                containers.add(loader.getExtension(args[i]));

+            }

+            logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");

+            

+            Runtime.getRuntime().addShutdownHook(new Thread() {

+                public void run() {

+                    for (Container container : containers) {

+                        try {

+                            container.stop();

+                            logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");

+                        } catch (Throwable t) {

+                            logger.error(t.getMessage(), t);

+                        }

+                        synchronized (Main.class) {

+                            running = false;

+                            Main.class.notify();

+                        }

+                    }

+                }

+            });

+            

+            for (Container container : containers) {

+                container.start();

+                logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");

+            }

+            System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");

+        } catch (RuntimeException e) {

+            e.printStackTrace();

+            logger.error(e.getMessage(), e);

+            System.exit(1);

+        }

+        synchronized (Main.class) {

+            while (running) {

+                try {

+                    Main.class.wait();

+                } catch (Throwable e) {

+                }

+            }

+        }

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.java
new file mode 100644
index 0000000..e9fae86
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.java
@@ -0,0 +1,95 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.jetty;

+

+import org.mortbay.jetty.Handler;

+import org.mortbay.jetty.Server;

+import org.mortbay.jetty.nio.SelectChannelConnector;

+import org.mortbay.jetty.servlet.FilterHolder;

+import org.mortbay.jetty.servlet.ServletHandler;

+import org.mortbay.jetty.servlet.ServletHolder;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.container.Container;

+import com.alibaba.dubbo.container.page.PageServlet;

+import com.alibaba.dubbo.container.page.ResourceFilter;

+

+/**

+ * JettyContainer. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class JettyContainer implements Container {

+

+    private static final Logger logger = LoggerFactory.getLogger(JettyContainer.class);

+

+    public static final String JETTY_PORT = "dubbo.jetty.port";

+

+    public static final String JETTY_DIRECTORY = "dubbo.jetty.directory";

+

+    public static final String JETTY_PAGES = "dubbo.jetty.page";

+

+    public static final int DEFAULT_JETTY_PORT = 8080;

+

+    SelectChannelConnector connector;

+

+    public void start() {

+        String serverPort = ConfigUtils.getProperty(JETTY_PORT);

+        int port;

+        if (serverPort == null || serverPort.length() == 0) {

+            port = DEFAULT_JETTY_PORT;

+        } else {

+            port = Integer.parseInt(serverPort);

+        }

+        connector = new SelectChannelConnector();

+        connector.setPort(port);

+        ServletHandler handler = new ServletHandler();

+        

+        String resources = ConfigUtils.getProperty(JETTY_DIRECTORY);

+        if (resources != null && resources.length() > 0) {

+            FilterHolder resourceHolder = handler.addFilterWithMapping(ResourceFilter.class, "/*", Handler.DEFAULT);

+            resourceHolder.setInitParameter("resources", resources);

+        }

+        

+        ServletHolder pageHolder = handler.addServletWithMapping(PageServlet.class, "/*");

+        pageHolder.setInitParameter("pages", ConfigUtils.getProperty(JETTY_PAGES));

+        pageHolder.setInitOrder(2);

+        

+        Server server = new Server();

+        server.addConnector(connector);

+        server.addHandler(handler);

+        try {

+            server.start();

+        } catch (Exception e) {

+            throw new IllegalStateException("Failed to start jetty server on " + NetUtils.getLocalHost() + ":" + port + ", cause: " + e.getMessage(), e);

+        }

+    }

+

+    public void stop() {

+        try {

+            if (connector != null) {

+                connector.close();

+                connector = null;

+            }

+        } catch (Throwable e) {

+            logger.error(e.getMessage(), e);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.java
new file mode 100644
index 0000000..68e1335
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.java
@@ -0,0 +1,99 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.log4j;

+

+import java.util.Enumeration;

+import java.util.Properties;

+

+import org.apache.log4j.Appender;

+import org.apache.log4j.FileAppender;

+import org.apache.log4j.LogManager;

+import org.apache.log4j.PropertyConfigurator;

+

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.container.Container;

+

+/**

+ * Log4jContainer. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class Log4jContainer implements Container {

+

+    public static final String LOG4J_FILE = "dubbo.log4j.file";

+

+    public static final String LOG4J_LEVEL = "dubbo.log4j.level";

+

+    public static final String LOG4J_SUBDIRECTORY = "dubbo.log4j.subdirectory";

+

+    public static final String DEFAULT_LOG4J_LEVEL = "ERROR";

+

+    @SuppressWarnings("unchecked")

+    public void start() {

+        String file = ConfigUtils.getProperty(LOG4J_FILE);

+        if (file != null && file.length() > 0) {

+            String level = ConfigUtils.getProperty(LOG4J_LEVEL);

+            if (level == null || level.length() == 0) {

+                level = DEFAULT_LOG4J_LEVEL;

+            }

+            Properties properties = new Properties();

+            properties.setProperty("log4j.rootLogger", level + ",application");

+            properties.setProperty("log4j.appender.application", "org.apache.log4j.DailyRollingFileAppender");

+            properties.setProperty("log4j.appender.application.File", file);

+            properties.setProperty("log4j.appender.application.Append", "true");

+            properties.setProperty("log4j.appender.application.DatePattern", "'.'yyyy-MM-dd");

+            properties.setProperty("log4j.appender.application.layout", "org.apache.log4j.PatternLayout");

+            properties.setProperty("log4j.appender.application.layout.ConversionPattern", "%d [%t] %-5p %C{6} (%F:%L) - %m%n");

+            PropertyConfigurator.configure(properties);

+        }

+        String subdirectory = ConfigUtils.getProperty(LOG4J_SUBDIRECTORY);

+        if (subdirectory != null && subdirectory.length() > 0) {

+            Enumeration<org.apache.log4j.Logger> ls = LogManager.getCurrentLoggers();

+            while (ls.hasMoreElements()) {

+                org.apache.log4j.Logger l = ls.nextElement();

+                if (l != null) {

+                    Enumeration<Appender> as = l.getAllAppenders();

+                    while (as.hasMoreElements()) {

+                        Appender a = as.nextElement();

+                        if (a instanceof FileAppender) {

+                            FileAppender fa = (FileAppender)a;

+                            String f = fa.getFile();

+                            if (f != null && f.length() > 0) {

+                                int i = f.replace('\\', '/').lastIndexOf('/');

+                                String path;

+                                if (i == -1) {

+                                    path = subdirectory;

+                                } else {

+                                    path = f.substring(0, i);

+                                    if (! path.endsWith(subdirectory)) {

+                                        path = path + "/" + subdirectory;

+                                    }

+                                    f = f.substring(i + 1);

+                                }

+                                fa.setFile(path + "/" + f);

+                                fa.activateOptions();

+                            }

+                        }

+                    }

+                }

+            }

+        }

+    }

+

+    public void stop() {

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Menu.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Menu.java
new file mode 100644
index 0000000..4a32298
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Menu.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+/**

+ * Menu

+ * 

+ * @author william.liangf

+ */

+@Documented

+@Retention(RetentionPolicy.RUNTIME)

+@Target({ElementType.TYPE})

+public @interface Menu {

+    

+    String name();

+    

+    String desc() default "";

+    

+    int order() default 0;

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/MenuComparator.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/MenuComparator.java
new file mode 100644
index 0000000..7085ed4
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/MenuComparator.java
@@ -0,0 +1,44 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page;

+

+import java.io.Serializable;

+import java.util.Comparator;

+

+/**

+ * MenuComparator

+ * 

+ * @author william.liangf

+ */

+public class MenuComparator implements Comparator<PageHandler>, Serializable {

+

+    private static final long serialVersionUID = -3161526932904414029L;

+

+    public int compare(PageHandler o1, PageHandler o2) {

+        if (o1 == null && o2 == null) {

+            return 0;

+        }

+        if (o1 == null) {

+            return -1;

+        }

+        if (o2 == null) {

+            return 1;

+        }

+        return o1.equals(o2) ? 0 : (o1.getClass().getAnnotation(Menu.class).order() 

+                > o2.getClass().getAnnotation(Menu.class).order() ? 1 : -1);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Page.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Page.java
new file mode 100644
index 0000000..e843e37
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Page.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page;

+

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+

+/**

+ * Page

+ * 

+ * @author william.liangf

+ */

+public class Page {

+

+    private final String navigation;

+    

+    private final String title;

+

+    private final List<String> columns;

+

+    private final List<List<String>> rows;

+    

+    public Page(String navigation) {

+        this(navigation, (String) null, (String[]) null, (List<List<String>>) null);

+    }

+

+    public Page(String navigation, String title,

+                       String column, String row) {

+        this(navigation, title, column == null ? null : Arrays.asList(new String[]{column}), row == null ? null : stringToList(row));

+    }

+    

+    private static List<List<String>> stringToList(String str) {

+        List<List<String>> rows = new ArrayList<List<String>>();

+        List<String> row = new ArrayList<String>();

+        row.add(str);

+        rows.add(row);

+        return rows;

+    }

+

+    public Page(String navigation, String title,

+                       String[] columns, List<List<String>> rows) {

+        this(navigation, title, columns == null ? null : Arrays.asList(columns), rows);

+    }

+

+    public Page(String navigation, String title,

+                       List<String> columns, List<List<String>> rows) {

+        this.navigation = navigation;

+        this.title = title;

+        this.columns = columns;

+        this.rows = rows;

+    }

+

+    public String getNavigation() {

+        return navigation;

+    }

+

+    public String getTitle() {

+        return title;

+    }

+

+    public List<String> getColumns() {

+        return columns;

+    }

+

+    public List<List<String>> getRows() {

+        return rows;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java
new file mode 100644
index 0000000..f15c729
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * PageHandler

+ * 

+ * @author william.liangf

+ */

+@SPI

+public interface PageHandler {

+    

+    /**

+     * Handle the page.

+     * 

+     * @param url

+     * @return the page.

+     */

+    Page handle(URL url);

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java
new file mode 100644
index 0000000..ad009bb
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java
@@ -0,0 +1,286 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page;

+

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.List;

+import java.util.Map;

+import java.util.Random;

+import java.util.concurrent.ConcurrentHashMap;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.StringUtils;

+

+/**

+ * PageServlet

+ * 

+ * @author william.liangf

+ */

+public class PageServlet extends HttpServlet {

+

+    private static final long     serialVersionUID = -8370312705453328501L;

+

+    protected static final Logger logger           = LoggerFactory.getLogger(PageServlet.class);

+

+    protected final Random        random           = new Random();

+    

+    protected final Map<String, PageHandler>  pages = new ConcurrentHashMap<String, PageHandler>();

+

+    protected final List<PageHandler>    menus = new ArrayList<PageHandler>();

+    

+    private static PageServlet INSTANCE;

+    

+    public static PageServlet getInstance() {

+        return INSTANCE;

+    }

+

+    public List<PageHandler> getMenus() {

+        return Collections.unmodifiableList(menus);

+    }

+

+    @Override

+    public void init() throws ServletException {

+        super.init();

+        INSTANCE = this;

+        String config = getServletConfig().getInitParameter("pages");

+        Collection<String> names;

+        if (config != null && config.length() > 0) {

+            names = Arrays.asList(Constants.COMMA_SPLIT_PATTERN.split(config));

+        } else {

+            names = ExtensionLoader.getExtensionLoader(PageHandler.class).getSupportedExtensions();

+        }

+        for (String name : names) {

+            PageHandler handler = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtension(name);

+            pages.put(ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler), handler);

+            Menu menu = handler.getClass().getAnnotation(Menu.class);

+            if (menu != null) {

+                menus.add(handler);

+            }

+        }

+        Collections.sort(menus, new MenuComparator());

+    }

+    

+    @Override

+    protected final void doGet(HttpServletRequest request, HttpServletResponse response)

+            throws ServletException, IOException {

+        doPost(request, response);

+    }

+

+    @Override

+    protected final void doPost(HttpServletRequest request, HttpServletResponse response)

+            throws ServletException, IOException {

+        if (! response.isCommitted()) {

+            PrintWriter writer = response.getWriter();

+            String uri = request.getRequestURI();

+            boolean isHtml = false;

+            if (uri == null || uri.length() == 0 || "/".equals(uri)) {

+                uri = "index";

+                isHtml = true;

+            } else {

+                if (uri.startsWith("/")) {

+                    uri = uri.substring(1);

+                }

+                if (uri.endsWith(".html")) {

+                    uri = uri.substring(0, uri.length() - ".html".length());

+                    isHtml = true;

+                }

+            }

+            if (uri.endsWith("favicon.ico")) {

+                response.sendError(HttpServletResponse.SC_NOT_FOUND);

+                return;

+            }

+            ExtensionLoader<PageHandler> pageHandlerLoader = ExtensionLoader.getExtensionLoader(PageHandler.class);

+            PageHandler pageHandler = pageHandlerLoader.hasExtension(uri) ? pageHandlerLoader.getExtension(uri) : null;

+            if (isHtml) {

+                writer.println("<html><head><title>Dubbo</title>");

+                writer.println("<style type=\"text/css\">html, body {margin: 10;padding: 0;background-color: #6D838C;font-family: Arial, Verdana;font-size: 12px;color: #FFFFFF;text-align: center;vertical-align: middle;word-break: break-all; } table {width: 90%; margin: 0px auto;border-collapse: collapse;border: 8px solid #FFFFFF; } thead tr {background-color: #253c46; } tbody tr {background-color: #8da5af; } th {padding-top: 4px;padding-bottom: 4px;font-size: 14px;height: 20px; } td {margin: 3px;padding: 3px;border: 2px solid #FFFFFF;font-size: 14px;height: 25px; } a {color: #FFFFFF;cursor: pointer;text-decoration: underline; } a:hover {text-decoration: none; }</style>");

+                writer.println("</head><body>");

+            }

+            if (pageHandler != null) {

+                Page page = null;

+                try {

+                    String query = request.getQueryString();

+                    page = pageHandler.handle(URL.valueOf(request.getRequestURL().toString() 

+                            + (query == null || query.length() == 0 ? "" : "?" + query)));

+                } catch (Throwable t) {

+                    logger.warn(t.getMessage(), t);

+                    String msg = t.getMessage();

+                    if (msg == null) {

+                        msg = StringUtils.toString(t);

+                    }

+                    if (isHtml) {

+                        writer.println("<table>");

+                        writer.println("<thead>");

+                        writer.println("    <tr>");

+                        writer.println("        <th>Error</th>");

+                        writer.println("    </tr>");

+                        writer.println("</thead>");

+                        writer.println("<tbody>");

+                        writer.println("    <tr>");

+                        writer.println("        <td>");

+                        writer.println("            " + msg.replace("<", "&lt;").replace(">", "&lt;").replace("\n", "<br/>"));

+                        writer.println("        </td>");

+                        writer.println("    </tr>");

+                        writer.println("</tbody>");

+                        writer.println("</table>");

+                        writer.println("<br/>");

+                    } else {

+                        writer.println(msg);

+                    }

+                }

+                if (page != null) {

+                    if (isHtml) {

+                        String nav = page.getNavigation();

+                        if (nav == null || nav.length() == 0) {

+                            nav = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(pageHandler);

+                            nav = nav.substring(0, 1).toUpperCase() + nav.substring(1);

+                        }

+                        if (! "index".equals(uri)) {

+                            nav = "<a href=\"/\">Home</a> &gt; " + nav;

+                        }

+                        writeMenu(request, writer, nav);

+                        writeTable(writer, page.getTitle(), page.getColumns(),

+                                page.getRows());

+                    } else {

+                        if (page.getRows().size() > 0 && page.getRows().get(0).size() > 0) {

+                            writer.println(page.getRows().get(0).get(0));

+                        }

+                    }

+                }

+            } else {

+                if (isHtml) {

+                    writer.println("<table>");

+                    writer.println("<thead>");

+                    writer.println("    <tr>");

+                    writer.println("        <th>Error</th>");

+                    writer.println("    </tr>");

+                    writer.println("</thead>");

+                    writer.println("<tbody>");

+                    writer.println("    <tr>");

+                    writer.println("        <td>");

+                    writer.println("            Not found " + uri + " page. Please goto <a href=\"/\">Home</a> page.");

+                    writer.println("        </td>");

+                    writer.println("    </tr>");

+                    writer.println("</tbody>");

+                    writer.println("</table>");

+                    writer.println("<br/>");

+                } else {

+                    writer.println("Not found " + uri + " page.");

+                }

+            }

+            if (isHtml) {

+                writer.println("</body></html>");

+            }

+            writer.flush();

+        }

+    }

+

+    protected final void writeMenu(HttpServletRequest request, PrintWriter writer, String nav) {

+        writer.println("<table>");

+        writer.println("<thead>");

+        writer.println("    <tr>");

+        for (PageHandler handler : menus) {

+            String uri = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler);

+            Menu menu = handler.getClass().getAnnotation(Menu.class);

+            writer.println("        <th><a href=\"" + uri + ".html\">" + menu.name() + "</a></th>");

+        }

+        writer.println("    </tr>");

+        writer.println("</thead>");

+        writer.println("<tbody>");

+        writer.println("    <tr>");

+        writer.println("        <td style=\"text-align: left\" colspan=\"" + menus.size() + "\">");

+        writer.println(nav);

+        writer.println("        </td>");

+        writer.println("    </tr>");

+        writer.println("</tbody>");

+        writer.println("</table>");

+        writer.println("<br/>");

+    }

+

+    protected final void writeTable(PrintWriter writer, String title, List<String> columns,

+                                    List<List<String>> rows) {

+        int n = random.nextInt();

+        int c = (columns == null ? (rows == null || rows.size() == 0 ? 0 : rows.get(0).size())

+                : columns.size());

+        int r = (rows == null ? 0 : rows.size());

+        writer.println("<table>");

+        writer.println("<thead>");

+        writer.println("    <tr>");

+        writer.println("        <th colspan=\"" + c + "\">" + title + "</th>");

+        writer.println("    </tr>");

+        if (columns != null && columns.size() > 0) {

+            writer.println("    <tr>");

+            for (int i = 0; i < columns.size(); i++) {

+                String col = columns.get(i);

+                if (col.endsWith(":")) {

+                    col += " <input type=\"text\" id=\"in_"

+                            + n

+                            + "_"

+                            + i

+                            + "\" onkeyup=\"for (var i = 0; i < "

+                            + r

+                            + "; i ++) { var m = true; for (var j = 0; j < "

+                            + columns.size()

+                            + "; j ++) { if (document.getElementById('in_"

+                            + n

+                            + "_' + j)) { var iv = document.getElementById('in_"

+                            + n

+                            + "_' + j).value; var tv = document.getElementById('td_"

+                            + n

+                            + "_' + i + '_' + j).innerHTML; if (iv.length > 0 && (tv.length < iv.length || tv.indexOf(iv) == -1)) { m = false; break; } } } document.getElementById('tr_"

+                            + n

+                            + "_' + i).style.display = (m ? '' : 'none');}\" sytle=\"width: 100%\" />";

+                }

+                writer.println("        <td>" + col + "</td>");

+            }

+            writer.println("    </tr>");

+        }

+        writer.println("</thead>");

+        if (rows != null && rows.size() > 0) {

+            writer.println("<tbody>");

+            int i = 0;

+            for (Collection<String> row : rows) {

+                writer.println("    <tr id=\"tr_" + n + "_" + i + "\">");

+                int j = 0;

+                for (String col : row) {

+                    writer.println("        <td id=\"td_" + n + "_" + i + "_" + j

+                            + "\" style=\"display: ;\">" + col + "</td>");

+                    j++;

+                }

+                writer.println("    </tr>");

+                i++;

+            }

+            writer.println("</tbody>");

+        }

+        writer.println("</table>");

+        writer.println("<br/>");

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/ResourceFilter.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/ResourceFilter.java
new file mode 100644
index 0000000..f72679d
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/ResourceFilter.java
@@ -0,0 +1,153 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page;

+

+import java.io.ByteArrayOutputStream;

+import java.io.File;

+import java.io.FileInputStream;

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+import java.net.URL;

+import java.util.ArrayList;

+import java.util.List;

+

+import javax.servlet.Filter;

+import javax.servlet.FilterChain;

+import javax.servlet.FilterConfig;

+import javax.servlet.ServletException;

+import javax.servlet.ServletRequest;

+import javax.servlet.ServletResponse;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import com.alibaba.dubbo.common.Constants;

+

+/**

+ * ResourceServlet

+ * 

+ * @author william.liangf

+ */

+public class ResourceFilter implements Filter {

+

+    private static final String CLASSPATH_PREFIX = "classpath:";

+

+    private final long start = System.currentTimeMillis();

+

+    private final List<String> resources = new ArrayList<String>();

+

+    public void init(FilterConfig filterConfig) throws ServletException {

+        String config = filterConfig.getInitParameter("resources");

+        if (config != null && config.length() > 0) {

+            String[] configs = Constants.COMMA_SPLIT_PATTERN.split(config);

+            for (String c : configs) {

+                if (c != null && c.length() > 0) {

+                    c = c.replace('\\', '/');

+                    if (c.endsWith("/")) {

+                        c = c.substring(0, c.length() - 1);

+                    }

+                    resources.add(c);

+                }

+            }

+        }

+    }

+

+    public void destroy() {

+    }

+    

+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)

+            throws IOException, ServletException {

+        HttpServletRequest request = (HttpServletRequest) req;

+        HttpServletResponse response = (HttpServletResponse) res;

+        if (response.isCommitted()) {

+            return;

+        }

+        String uri = request.getRequestURI();

+        String context = request.getContextPath();

+        if (uri.endsWith("/favicon.ico")) {

+            uri = "/favicon.ico";

+        } else if (context != null && ! "/".equals(context)) {

+            uri = uri.substring(context.length());

+        }

+        if (! uri.startsWith("/")) {

+            uri = "/" + uri;

+        }

+        long lastModified = getLastModified(uri);

+        long since = request.getDateHeader("If-Modified-Since");

+        if (since >= lastModified) {

+        	response.sendError(HttpServletResponse.SC_NOT_MODIFIED);

+        	return;

+        }

+        byte[] data;

+        InputStream input = getInputStream(uri);

+    	if (input == null) {

+    	    chain.doFilter(req, res);

+            return;

+        }

+    	try {

+            ByteArrayOutputStream output = new ByteArrayOutputStream();

+            byte[] buffer = new byte[8192];

+            int n = 0;

+            while (-1 != (n = input.read(buffer))) {

+                output.write(buffer, 0, n);

+            }

+            data = output.toByteArray();

+        } finally {

+            input.close();

+        }

+        response.setDateHeader("Last-Modified", lastModified);

+        OutputStream output = response.getOutputStream();

+        output.write(data);

+        output.flush();

+    }

+    

+    private boolean isFile(String path) {

+        return path.startsWith("/") || path.indexOf(":") <= 1;

+    }

+	

+	private long getLastModified(String uri) {

+	    for (String resource : resources) {

+            if (resource != null && resource.length() > 0) {

+                String path = resource + uri;

+                if (isFile(path)) {

+                    File file = new File(path);

+                    if (file.exists()) {

+                        return file.lastModified();

+                    }

+                }

+            }

+        }

+        return start;

+	}

+	

+	private InputStream getInputStream(String uri) {

+        for (String resource : resources) {

+            String path = resource + uri;

+            try {

+                if (isFile(path)) {

+                    return new FileInputStream(path);

+                } else if (path.startsWith(CLASSPATH_PREFIX)) {

+                    return Thread.currentThread().getContextClassLoader().getResourceAsStream(path.substring(CLASSPATH_PREFIX.length()));

+                } else {

+                    return new URL(path).openStream();

+                }

+            } catch (IOException e) {

+            }

+        }

+        return null;

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.java
new file mode 100644
index 0000000..265f980
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.java
@@ -0,0 +1,49 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.container.page.Menu;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.container.page.PageServlet;

+

+/**

+ * HomePageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "Home", desc = "Home page.", order = Integer.MIN_VALUE)

+public class HomePageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        List<List<String>> rows = new ArrayList<List<String>>();

+        for (PageHandler handler : PageServlet.getInstance().getMenus()) {

+            String uri = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler);

+            Menu menu = handler.getClass().getAnnotation(Menu.class);

+            List<String> row = new ArrayList<String>();

+            row.add("<a href=\"" + uri + ".html\">" + menu.name() + "</a>");

+            row.add(menu.desc());

+            rows.add(row);

+        }

+        return new Page("Home", "Menus",  new String[] {"Menu Name", "Menu Desc"}, rows);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java
new file mode 100644
index 0000000..8891eb2
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java
@@ -0,0 +1,107 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page.pages;

+

+import java.io.File;

+import java.io.FileInputStream;

+import java.io.IOException;

+import java.nio.ByteBuffer;

+import java.nio.channels.FileChannel;

+import java.text.SimpleDateFormat;

+import java.util.ArrayList;

+import java.util.Date;

+import java.util.Enumeration;

+import java.util.List;

+

+import org.apache.log4j.Appender;

+import org.apache.log4j.FileAppender;

+import org.apache.log4j.Level;

+import org.apache.log4j.LogManager;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Menu;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+

+/**

+ * LogPageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "Log", desc = "Show system log.", order = Integer.MAX_VALUE - 11000)

+public class LogPageHandler implements PageHandler {

+

+    private static final int SHOW_LOG_LENGTH = 30000;

+

+    private File            file;

+    

+    @SuppressWarnings("unchecked")

+	public LogPageHandler() {

+	    try {

+			org.apache.log4j.Logger logger = LogManager.getRootLogger();

+	        if (logger != null) {

+	            Enumeration<Appender> appenders = logger.getAllAppenders();

+	            if (appenders != null) {

+	                while (appenders.hasMoreElements()) {

+	                    Appender appender = appenders.nextElement();

+	                    if (appender instanceof FileAppender) {

+	                        FileAppender fileAppender = (FileAppender)appender;

+	                        String filename = fileAppender.getFile();

+	                        file = new File(filename);

+	                        break;

+	                    }

+	                }

+	            }

+	        }

+	    } catch (Throwable t) {

+	    }

+    }

+

+    public Page handle(URL url) {

+        long size = 0;

+		String content = "";

+		String modified = "Not exist";

+		if (file != null && file.exists()) {

+			try {

+				FileInputStream fis = new FileInputStream(file);

+				FileChannel channel = fis.getChannel();

+				size = channel.size();

+				ByteBuffer bb;

+				if (size <= SHOW_LOG_LENGTH) {

+					bb = ByteBuffer.allocate((int) size);

+					channel.read(bb, 0);

+				} else {

+					int pos = (int) (size - SHOW_LOG_LENGTH);

+					bb = ByteBuffer.allocate(SHOW_LOG_LENGTH);

+					channel.read(bb, pos);

+				}

+				bb.flip();

+				content = new String(bb.array()).replace("<", "&lt;")

+						.replace(">", "&gt;").replace("\n", "<br/><br/>");

+				modified = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

+						.format(new Date(file.lastModified()));

+			} catch (IOException e) {

+			}

+		}

+		Level level = LogManager.getRootLogger().getLevel();

+        List<List<String>> rows = new ArrayList<List<String>>();

+        List<String> row = new ArrayList<String>();

+        row.add(content);

+        rows.add(row);

+        return new Page("Log", "Log",  new String[] {(file == null ? "" : file.getName()) + ", " + size + " bytes, " + modified + ", " + level}, rows);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.java
new file mode 100644
index 0000000..f22da9b
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.java
@@ -0,0 +1,85 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page.pages;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.common.status.support.StatusUtils;

+import com.alibaba.dubbo.container.page.Menu;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+

+/**

+ * StatusPageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "Status", desc = "Show system status.", order = Integer.MAX_VALUE - 12000)

+public class StatusPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        List<List<String>> rows = new ArrayList<List<String>>();

+        Set<String> names = ExtensionLoader.getExtensionLoader(StatusChecker.class).getSupportedExtensions();

+        Map<String, Status> statuses = new HashMap<String, Status>();

+        for (String name : names) {

+            StatusChecker checker = ExtensionLoader.getExtensionLoader(StatusChecker.class).getExtension(name);

+            List<String> row = new ArrayList<String>();

+            row.add(name);

+            Status status = checker.check();

+            if (status != null && ! Status.Level.UNKNOWN.equals(status.getLevel())) {

+                statuses.put(name, status);

+                row.add(getLevelHtml(status.getLevel()));

+                row.add(status.getMessage());

+                rows.add(row);

+            }

+        }

+        Status status = StatusUtils.getSummaryStatus(statuses);

+        if ("status".equals(url.getPath())) {

+            return new Page("", "", "", status.getLevel().toString());

+        } else {

+            List<String> row = new ArrayList<String>();

+            row.add("summary");

+            row.add(getLevelHtml(status.getLevel()));

+            row.add("<a href=\"/status\" target=\"_blank\">summary</a>");

+            rows.add(row);

+            return new Page("Status (<a href=\"/status\" target=\"_blank\">summary</a>)", "Status", new String[] {"Name", "Status", "Description"}, rows);

+        }

+    }

+

+    private String getLevelHtml(Status.Level level) {

+        return "<font color=\"" + getLevelColor(level) + "\">" + level.name() + "</font>";

+    }

+

+    private String getLevelColor(Status.Level level) {

+        if (level == Status.Level.OK) {

+            return "green";

+        } else if (level == Status.Level.ERROR) {

+            return "red";

+        } else if (level == Status.Level.WARN) {

+            return "yellow";

+        }

+        return "gray";

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java
new file mode 100644
index 0000000..aa8c45a
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java
@@ -0,0 +1,141 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.page.pages;

+

+import java.lang.management.ManagementFactory;

+import java.text.SimpleDateFormat;

+import java.util.ArrayList;

+import java.util.Date;

+import java.util.List;

+import java.util.Locale;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.container.page.Menu;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+

+/**

+ * SystemPageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "System", desc = "Show system environment information.", order = Integer.MAX_VALUE - 10000)

+public class SystemPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        List<List<String>> rows = new ArrayList<List<String>>();

+        List<String> row;

+        

+        row = new ArrayList<String>();

+        row.add("Version");

+        row.add(Version.getVersion(SystemPageHandler.class, "2.0.0"));

+        rows.add(row);

+

+        row = new ArrayList<String>();

+        row.add("Host");

+        String address = NetUtils.getLocalHost();

+        row.add(NetUtils.getHostName(address) + "/" + address);

+        rows.add(row);

+        

+        row = new ArrayList<String>();

+        row.add("OS");

+        row.add(System.getProperty("os.name") + " " + System.getProperty("os.version"));

+        rows.add(row);

+        

+        row = new ArrayList<String>();

+        row.add("JVM");

+        row.add(System.getProperty("java.runtime.name") + " " + System.getProperty("java.runtime.version") + ",<br/>" + System.getProperty("java.vm.name") + " " + System.getProperty("java.vm.version") + " " + System.getProperty("java.vm.info", ""));

+        rows.add(row);

+        

+        row = new ArrayList<String>();

+        row.add("CPU");

+        row.add(System.getProperty("os.arch", "") + ", " + String.valueOf(Runtime.getRuntime().availableProcessors()) + " cores");

+        rows.add(row);

+        

+        row = new ArrayList<String>();

+        row.add("Locale");

+        row.add(Locale.getDefault().toString() + "/" + System.getProperty("file.encoding"));

+        rows.add(row);

+        

+        row = new ArrayList<String>();

+        row.add("Uptime");

+        row.add(formatUptime(ManagementFactory.getRuntimeMXBean().getUptime()));

+        rows.add(row);

+

+        row = new ArrayList<String>();

+        row.add("Time");

+        row.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z").format(new Date()));

+        rows.add(row);

+        

+        return new Page("System", "System", new String[] {

+                "Property", "Value" }, rows);

+    }

+

+    private static final long SECOND = 1000;

+    

+    private static final long MINUTE = 60 * SECOND;

+    

+    private static final long HOUR = 60 * MINUTE;

+    

+    private static final long DAY = 24 * HOUR;

+    

+    private String formatUptime(long uptime) {

+        StringBuilder buf = new StringBuilder();

+        if (uptime > DAY) {

+            long days = (uptime - uptime % DAY) / DAY;

+            buf.append(days);

+            buf.append(" Days");

+            uptime = uptime % DAY;

+        }

+        if (uptime > HOUR) {

+            long hours = (uptime - uptime % HOUR) / HOUR;

+            if (buf.length() > 0) {

+                buf.append(", ");

+            }

+            buf.append(hours);

+            buf.append(" Hours");

+            uptime = uptime % HOUR;

+        }

+        if (uptime > MINUTE) {

+            long minutes = (uptime - uptime % MINUTE) / MINUTE;

+            if (buf.length() > 0) {

+                buf.append(", ");

+            }

+            buf.append(minutes);

+            buf.append(" Minutes");

+            uptime = uptime % MINUTE;

+        }

+        if (uptime > SECOND) {

+            long seconds = (uptime - uptime % SECOND) / SECOND;

+            if (buf.length() > 0) {

+                buf.append(", ");

+            }

+            buf.append(seconds);

+            buf.append(" Seconds");

+            uptime = uptime % SECOND;

+        }

+        if (uptime > 0) {

+            if (buf.length() > 0) {

+                buf.append(", ");

+            }

+            buf.append(uptime);

+            buf.append(" Milliseconds");

+        }

+        return buf.toString();

+    }

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java
new file mode 100644
index 0000000..c955259
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java
@@ -0,0 +1,61 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.spring;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.container.Container;

+

+/**

+ * SpringContainer. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class SpringContainer implements Container {

+

+    private static final Logger logger = LoggerFactory.getLogger(SpringContainer.class);

+

+    public static final String SPRING_CONFIG = "dubbo.spring.config";

+    

+    public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml";

+

+    ClassPathXmlApplicationContext context;

+    

+    public void start() {

+        String configPath = ConfigUtils.getProperty(SPRING_CONFIG);

+        if (configPath == null || configPath.length() == 0) {

+            configPath = DEFAULT_SPRING_CONFIG;

+        }

+        context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"));

+        context.start();

+    }

+

+    public void stop() {

+        try {

+            if (context != null) {

+                context.stop();

+                context.close();

+                context = null;

+            }

+        } catch (Throwable e) {

+            logger.error(e.getMessage(), e);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/resources/META-INF/assembly/bin/dump.sh b/dubbo-container/src/main/resources/META-INF/assembly/bin/dump.sh
new file mode 100644
index 0000000..2458c43
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/assembly/bin/dump.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+cd `dirname $0`
+BIN_DIR=`pwd`
+cd ..
+DEPLOY_DIR=`pwd`
+CONF_DIR=$DEPLOY_DIR/conf
+
+SERVER_NAME=`sed '/dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
+LOGS_FILE=`sed '/dubbo.log4j.file/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
+
+if [ -z "$SERVER_NAME" ]; then
+	SERVER_NAME=`hostname`
+fi
+
+PIDS=`ps  --no-heading -C java -f --width 1000 | grep "$CONF_DIR" |awk '{print $2}'`
+if [ -z "$PIDS" ]; then
+    echo "ERROR: The $SERVER_NAME does not started!"
+    exit 1
+fi
+
+LOGS_DIR=""
+if [ -n "$LOGS_FILE" ]; then
+	LOGS_DIR=`dirname $LOGS_FILE`
+else
+	LOGS_DIR=$DEPLOY_DIR/logs
+fi
+if [ ! -d $LOGS_DIR ]; then
+	mkdir $LOGS_DIR
+fi
+DUMP_DIR=$LOGS_DIR/dump
+if [ ! -d $DUMP_DIR ]; then
+	mkdir $DUMP_DIR
+fi
+DUMP_DATE=`date +%Y%m%d%H%M%S`
+DATE_DIR=$DUMP_DIR/$DUMP_DATE
+if [ ! -d $DATE_DIR ]; then
+	mkdir $DATE_DIR
+fi
+
+echo -e "Dumping the $SERVER_NAME ...\c"
+for PID in $PIDS ; do
+	jstack $PID > $DATE_DIR/jstack-$PID.dump 2>&1
+	echo -e ".\c"
+	jinfo $PID > $DATE_DIR/jinfo-$PID.dump 2>&1
+	echo -e ".\c"
+	jstat -gcutil $PID > $DATE_DIR/jstat-gcutil-$PID.dump 2>&1
+	echo -e ".\c"
+	jstat -gccapacity $PID > $DATE_DIR/jstat-gccapacity-$PID.dump 2>&1
+	echo -e ".\c"
+	jmap $PID > $DATE_DIR/jmap-$PID.dump 2>&1
+	echo -e ".\c"
+	jmap -heap $PID > $DATE_DIR/jmap-heap-$PID.dump 2>&1
+	echo -e ".\c"
+	jmap -histo $PID > $DATE_DIR/jmap-histo-$PID.dump 2>&1
+	echo -e ".\c"
+	if [ -r /usr/sbin/lsof ]; then
+	/usr/sbin/lsof -p $PID > $DATE_DIR/lsof-$PID.dump
+	echo -e ".\c"
+	fi
+done
+if [ -r /bin/netstat ]; then
+/bin/netstat -an > $DATE_DIR/netstat.dump 2>&1
+echo -e ".\c"
+fi
+if [ -r /usr/bin/iostat ]; then
+/usr/bin/iostat > $DATE_DIR/iostat.dump 2>&1
+echo -e ".\c"
+fi
+if [ -r /usr/bin/mpstat ]; then
+/usr/bin/mpstat > $DATE_DIR/mpstat.dump 2>&1
+echo -e ".\c"
+fi
+if [ -r /usr/bin/vmstat ]; then
+/usr/bin/vmstat > $DATE_DIR/vmstat.dump 2>&1
+echo -e ".\c"
+fi
+if [ -r /usr/bin/free ]; then
+/usr/bin/free -t > $DATE_DIR/free.dump 2>&1
+echo -e ".\c"
+fi
+if [ -r /usr/bin/sar ]; then
+/usr/bin/sar > $DATE_DIR/sar.dump 2>&1
+echo -e ".\c"
+fi
+if [ -r /usr/bin/uptime ]; then
+/usr/bin/uptime > $DATE_DIR/uptime.dump 2>&1
+echo -e ".\c"
+fi
+echo "OK!"
+echo "DUMP: $DATE_DIR"
diff --git a/dubbo-container/src/main/resources/META-INF/assembly/bin/restart.sh b/dubbo-container/src/main/resources/META-INF/assembly/bin/restart.sh
new file mode 100644
index 0000000..647ec19
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/assembly/bin/restart.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+cd `dirname $0`
+./stop.sh
+./start.sh
diff --git a/dubbo-container/src/main/resources/META-INF/assembly/bin/server.sh b/dubbo-container/src/main/resources/META-INF/assembly/bin/server.sh
new file mode 100644
index 0000000..90947a5
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/assembly/bin/server.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+cd `dirname $0`
+if [ "$1" = "start" ]; then
+	./start.sh
+else
+	if [ "$1" = "stop" ]; then
+		./stop.sh
+	else
+		if [ "$1" = "debug" ]; then
+			./start.sh debug
+		else
+			if [ "$1" = "restart" ]; then
+				./restart.sh
+			else
+				if [ "$1" = "dump" ]; then
+					./dump.sh
+				else
+					echo "ERROR: Please input argument: start or stop or debug or restart or dump"
+				    exit 1
+				fi
+			fi
+		fi
+	fi
+fi
diff --git a/dubbo-container/src/main/resources/META-INF/assembly/bin/start.bat b/dubbo-container/src/main/resources/META-INF/assembly/bin/start.bat
new file mode 100644
index 0000000..f91d023
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/assembly/bin/start.bat
@@ -0,0 +1,22 @@
+@echo off & setlocal enabledelayedexpansion

+

+set LIB_JARS=""

+cd ..\lib

+for %%i in (*) do set LIB_JARS=!LIB_JARS!;..\lib\%%i

+cd ..\bin

+

+if ""%1"" == ""debug"" goto debug

+if ""%1"" == ""jmx"" goto jmx

+

+java -Xms64m -Xmx1024m -XX:MaxPermSize=64M -classpath ..\conf;%LIB_JARS% com.alibaba.dubbo.container.Main

+goto end

+

+:debug

+java -Xms64m -Xmx1024m -XX:MaxPermSize=64M -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n -classpath ..\conf;%LIB_JARS% com.alibaba.dubbo.container.Main

+goto end

+

+:jmx

+java -Xms64m -Xmx1024m -XX:MaxPermSize=64M -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -classpath ..\conf;%LIB_JARS% com.alibaba.dubbo.container.Main

+

+:end

+pause
\ No newline at end of file
diff --git a/dubbo-container/src/main/resources/META-INF/assembly/bin/start.sh b/dubbo-container/src/main/resources/META-INF/assembly/bin/start.sh
new file mode 100644
index 0000000..e8b2756
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/assembly/bin/start.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+cd `dirname $0`
+BIN_DIR=`pwd`
+cd ..
+DEPLOY_DIR=`pwd`
+CONF_DIR=$DEPLOY_DIR/conf
+
+SERVER_NAME=`sed '/dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
+SERVER_PORT=`sed '/dubbo.protocol.port/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
+LOGS_FILE=`sed '/dubbo.log4j.file/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
+
+if [ -z "$SERVER_NAME" ]; then
+	SERVER_NAME=`hostname`
+fi
+
+PIDS=`ps  --no-heading -C java -f --width 1000 | grep "$CONF_DIR" |awk '{print $2}'`
+if [ -n "$PIDS" ]; then
+    echo "ERROR: The $SERVER_NAME already started!"
+    echo "PID: $PIDS"
+    exit 1
+fi
+
+if [ -n "$SERVER_PORT" ]; then
+	SERVER_PORT_COUNT=`netstat -tln | grep $SERVER_PORT | wc -l`
+	if [ $SERVER_PORT_COUNT -gt 0 ]; then
+		echo "ERROR: The $SERVER_NAME port $SERVER_PORT already used!"
+		exit 1
+	fi
+fi
+
+LOGS_DIR=""
+if [ -n "$LOGS_FILE" ]; then
+	LOGS_DIR=`dirname $LOGS_FILE`
+else
+	LOGS_DIR=$DEPLOY_DIR/logs
+fi
+if [ ! -d $LOGS_DIR ]; then
+	mkdir $LOGS_DIR
+fi
+STDOUT_FILE=$LOGS_DIR/stdout.log
+
+LIB_DIR=$DEPLOY_DIR/lib
+LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'|tr "\n" ":"`
+
+JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
+JAVA_DEBUG_OPTS=""
+if [ "$1" = "debug" ]; then
+    JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n "
+fi
+JAVA_JMX_OPTS=""
+if [ "$1" = "jmx" ]; then
+    JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false "
+fi
+JAVA_MEM_OPTS=""
+BITS=`file $JAVA_HOME/bin/java | grep 64-bit`
+if [ -n "$BITS" ]; then
+    let memTotal=`cat /proc/meminfo |grep MemTotal|awk '{printf "%d", $2/1024 }'`
+    if [ $memTotal -gt 2500 ];then
+        JAVA_MEM_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "
+    else 
+        JAVA_MEM_OPTS=" -server -Xmx1g -Xms1g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "
+    fi
+else
+	JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m -XX:SurvivorRatio=2 -XX:+UseParallelGC "
+fi
+
+echo -e "Starting the $SERVER_NAME ...\c"
+nohup java $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS -classpath $CONF_DIR:$LIB_JARS com.alibaba.dubbo.container.Main > $STDOUT_FILE 2>&1 &
+
+COUNT=0
+while [ $COUNT -lt 1 ]; do    
+    echo -e ".\c"
+    sleep 1 
+    if [ -n "$SERVER_PORT" ]; then
+    	COUNT=`echo status | nc 127.0.0.1 $SERVER_PORT -i 1 | grep -c OK`
+    else
+    	COUNT=`ps  --no-heading -C java -f --width 1000 | grep "$DEPLOY_DIR" | awk '{print $2}' | wc -l`
+    fi
+	if [ $COUNT -gt 0 ]; then
+		break
+	fi
+done
+echo "OK!"
+PIDS=`ps  --no-heading -C java -f --width 1000 | grep "$DEPLOY_DIR" | awk '{print $2}'`
+echo "PID: $PIDS"
+echo "STDOUT: $STDOUT_FILE"
diff --git a/dubbo-container/src/main/resources/META-INF/assembly/bin/stop.sh b/dubbo-container/src/main/resources/META-INF/assembly/bin/stop.sh
new file mode 100644
index 0000000..506ee0a
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/assembly/bin/stop.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+cd `dirname $0`
+BIN_DIR=`pwd`
+cd ..
+DEPLOY_DIR=`pwd`
+CONF_DIR=$DEPLOY_DIR/conf
+
+SERVER_NAME=`sed '/dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
+
+if [ -z "$SERVER_NAME" ]; then
+	SERVER_NAME=`hostname`
+fi
+
+PIDS=`ps  --no-heading -C java -f --width 1000 | grep "$CONF_DIR" |awk '{print $2}'`
+if [ -z "$PIDS" ]; then
+    echo "ERROR: The $SERVER_NAME does not started!"
+    exit 1
+fi
+
+if [ "$1" != "skip" ]; then
+$BIN_DIR/dump.sh
+fi
+
+echo -e "Stopping the $SERVER_NAME ...\c"
+for PID in $PIDS ; do
+	kill $PID > /dev/null 2>&1
+done
+
+COUNT=0
+while [ $COUNT -lt 1 ]; do    
+    echo -e ".\c"
+    sleep 1
+    COUNT=1
+    for PID in $PIDS ; do
+		PID_EXIST=`ps --no-heading -p $PID`
+		if [ -n "$PID_EXIST" ]; then
+			COUNT=0
+			break
+		fi
+	done
+done
+echo "OK!"
+echo "PID: $PIDS"
diff --git a/dubbo-container/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container b/dubbo-container/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..c825208
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
@@ -0,0 +1,3 @@
+spring=com.alibaba.dubbo.container.spring.SpringContainer

+jetty=com.alibaba.dubbo.container.jetty.JettyContainer

+log4j=com.alibaba.dubbo.container.log4j.Log4jContainer
\ No newline at end of file
diff --git a/dubbo-container/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler b/dubbo-container/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..e1c39d6
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,4 @@
+index=com.alibaba.dubbo.container.page.pages.HomePageHandler

+status=com.alibaba.dubbo.container.page.pages.StatusPageHandler

+log=com.alibaba.dubbo.container.page.pages.LogPageHandler

+system=com.alibaba.dubbo.container.page.pages.SystemPageHandler
\ No newline at end of file
diff --git a/dubbo-container/src/test/java/com/alibaba/dubbo/container/jetty/JettyContainerTest.java b/dubbo-container/src/test/java/com/alibaba/dubbo/container/jetty/JettyContainerTest.java
new file mode 100644
index 0000000..9b2c009
--- /dev/null
+++ b/dubbo-container/src/test/java/com/alibaba/dubbo/container/jetty/JettyContainerTest.java
@@ -0,0 +1,39 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.jetty;

+

+import org.junit.Assert;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.container.Container;

+

+/**

+ * StandaloneContainerTest

+ * 

+ * @author william.liangf

+ */

+public class JettyContainerTest {

+    

+    @Test

+    public void testContainer() {

+        JettyContainer container = (JettyContainer) ExtensionLoader.getExtensionLoader(Container.class).getExtension("jetty");

+        container.start();

+        Assert.assertTrue(container.connector.isStarted());

+        container.stop();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/test/java/com/alibaba/dubbo/container/log4j/Log4jContainerTest.java b/dubbo-container/src/test/java/com/alibaba/dubbo/container/log4j/Log4jContainerTest.java
new file mode 100644
index 0000000..6d68eb4
--- /dev/null
+++ b/dubbo-container/src/test/java/com/alibaba/dubbo/container/log4j/Log4jContainerTest.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.log4j;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.container.Container;

+

+/**

+ * StandaloneContainerTest

+ * 

+ * @author william.liangf

+ */

+public class Log4jContainerTest {

+    

+    @Test

+    public void testContainer() {

+        Log4jContainer container = (Log4jContainer) ExtensionLoader.getExtensionLoader(Container.class).getExtension("log4j");

+        container.start();

+        container.stop();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/test/java/com/alibaba/dubbo/container/spring/SpringContainerTest.java b/dubbo-container/src/test/java/com/alibaba/dubbo/container/spring/SpringContainerTest.java
new file mode 100644
index 0000000..678d3a1
--- /dev/null
+++ b/dubbo-container/src/test/java/com/alibaba/dubbo/container/spring/SpringContainerTest.java
@@ -0,0 +1,39 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.container.spring;

+

+import org.junit.Assert;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.container.Container;

+

+/**

+ * StandaloneContainerTest

+ * 

+ * @author william.liangf

+ */

+public class SpringContainerTest {

+    

+    @Test

+    public void testContainer() {

+        SpringContainer container = (SpringContainer) ExtensionLoader.getExtensionLoader(Container.class).getExtension("spring");

+        container.start();

+        Assert.assertEquals(SpringContainer.class, container.context.getBean("container").getClass());

+        container.stop();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-container/src/test/resources/META-INF/spring/test.xml b/dubbo-container/src/test/resources/META-INF/spring/test.xml
new file mode 100644
index 0000000..4ddff13
--- /dev/null
+++ b/dubbo-container/src/test/resources/META-INF/spring/test.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

+	

+	<bean id="container" class="com.alibaba.dubbo.container.spring.SpringContainer" />

+	

+</beans>
\ No newline at end of file
diff --git a/dubbo-demo-consumer/pom.xml b/dubbo-demo-consumer/pom.xml
new file mode 100644
index 0000000..983cad0
--- /dev/null
+++ b/dubbo-demo-consumer/pom.xml
@@ -0,0 +1,113 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-demo-consumer</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Demo Consumer Module</name>
+	<description>The demo consumer module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>false</skip_maven_deploy>

+	</properties>
+	<dependencies>
+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-demo</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>org.mortbay.jetty</groupId>

+			<artifactId>jetty</artifactId>

+		</dependency>

+		<dependency>

+		    <groupId>org.apache.zookeeper</groupId>

+		    <artifactId>zookeeper</artifactId>

+		</dependency>
+		<dependency>

+			<groupId>org.apache.mina</groupId>

+			<artifactId>mina-core</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>org.glassfish.grizzly</groupId>

+			<artifactId>grizzly-core</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.httpcomponents</groupId>

+			<artifactId>httpclient</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>com.caucho</groupId>

+			<artifactId>hessian</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>fastjson</artifactId>

+		</dependency>
+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<artifactId>maven-dependency-plugin</artifactId>

+				<executions>

+					<execution>

+						<id>unpack</id>

+						<phase>package</phase>

+						<goals>

+							<goal>unpack</goal>

+						</goals>

+						<configuration>

+							<artifactItems>

+								<artifactItem>

+									<groupId>com.alibaba</groupId>

+									<artifactId>dubbo</artifactId>

+									<version>${project.parent.version}</version>

+									<outputDirectory>${project.build.directory}/dubbo</outputDirectory>

+									<includes>META-INF/assembly/**</includes>

+								</artifactItem>

+							</artifactItems>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+			<plugin>

+				<artifactId>maven-assembly-plugin</artifactId>

+                <configuration>

+                    <descriptor>src/main/assembly/assembly.xml</descriptor>

+                </configuration>

+                <executions>

+					<execution>

+						<id>make-assembly</id>

+						<phase>package</phase>

+						<goals>

+							<goal>single</goal>

+						</goals>

+					</execution>

+				</executions>

+            </plugin>

+		</plugins>

+	</build>
+</project>
\ No newline at end of file
diff --git a/dubbo-demo-consumer/src/main/assembly/assembly.xml b/dubbo-demo-consumer/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..25235d9
--- /dev/null
+++ b/dubbo-demo-consumer/src/main/assembly/assembly.xml
@@ -0,0 +1,39 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<assembly>

+	<id>assembly</id>

+	<formats>

+		<format>tar.gz</format>

+	</formats>

+	<includeBaseDirectory>true</includeBaseDirectory>

+	<fileSets>

+		<fileSet>

+			<directory>${project.build.directory}/dubbo/META-INF/assembly/bin</directory>

+			<outputDirectory>bin</outputDirectory>

+			<fileMode>0755</fileMode>

+		</fileSet>

+		<fileSet>

+			<directory>src/main/assembly/conf</directory>

+			<outputDirectory>conf</outputDirectory>

+			<fileMode>0644</fileMode>

+		</fileSet>

+	</fileSets>

+	<dependencySets>

+		<dependencySet>

+			<outputDirectory>lib</outputDirectory>

+		</dependencySet>

+	</dependencySets>

+</assembly>
\ No newline at end of file
diff --git a/dubbo-demo-consumer/src/main/assembly/conf/dubbo.properties b/dubbo-demo-consumer/src/main/assembly/conf/dubbo.properties
new file mode 100644
index 0000000..d3cc21f
--- /dev/null
+++ b/dubbo-demo-consumer/src/main/assembly/conf/dubbo.properties
@@ -0,0 +1,25 @@
+##

+# Copyright 1999-2011 Alibaba Group.

+#  

+# 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.

+##

+dubbo.container=log4j,spring

+dubbo.application.name=demo-consumer

+dubbo.application.owner=

+dubbo.registry.address=multicast://224.5.6.7:1234

+#dubbo.registry.address=zookeeper://127.0.0.1:2181

+#dubbo.registry.address=redis://127.0.0.1:6379

+#dubbo.registry.address=dubbo://127.0.0.1:9090

+dubbo.monitor.protocol=registry

+dubbo.log4j.file=logs/dubbo-demo-consumer.log

+dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-demo-consumer/src/main/java/com/alibaba/dubbo/demo/consumer/DemoAction.java b/dubbo-demo-consumer/src/main/java/com/alibaba/dubbo/demo/consumer/DemoAction.java
new file mode 100644
index 0000000..ecc57de
--- /dev/null
+++ b/dubbo-demo-consumer/src/main/java/com/alibaba/dubbo/demo/consumer/DemoAction.java
@@ -0,0 +1,43 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.demo.consumer;

+

+import java.text.SimpleDateFormat;

+import java.util.Date;

+

+import com.alibaba.dubbo.demo.DemoService;

+

+public class DemoAction {

+    

+    private DemoService demoService;

+

+    public void setDemoService(DemoService demoService) {

+        this.demoService = demoService;

+    }

+

+	public void start() {

+        for (int i = 0; i < Integer.MAX_VALUE; i ++) {

+            try {

+            	String hello = demoService.sayHello("world" + i);

+                System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] " + hello);

+                Thread.sleep(2000);

+            } catch (Exception e) {

+                e.printStackTrace();

+            }

+        }

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-demo-consumer/src/main/resources/META-INF/spring/dubbo-demo-action.xml b/dubbo-demo-consumer/src/main/resources/META-INF/spring/dubbo-demo-action.xml
new file mode 100644
index 0000000..365bafb
--- /dev/null
+++ b/dubbo-demo-consumer/src/main/resources/META-INF/spring/dubbo-demo-action.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+	<bean class="com.alibaba.dubbo.demo.consumer.DemoAction" init-method="start">

+		<property name="demoService" ref="demoService" />

+	</bean>

+

+</beans>
\ No newline at end of file
diff --git a/dubbo-demo-consumer/src/main/resources/META-INF/spring/dubbo-demo-consumer.xml b/dubbo-demo-consumer/src/main/resources/META-INF/spring/dubbo-demo-consumer.xml
new file mode 100644
index 0000000..981599f
--- /dev/null
+++ b/dubbo-demo-consumer/src/main/resources/META-INF/spring/dubbo-demo-consumer.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+	<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" />

+

+</beans>
\ No newline at end of file
diff --git a/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/DemoConsumer.java b/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/DemoConsumer.java
new file mode 100644
index 0000000..3e414ff
--- /dev/null
+++ b/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/DemoConsumer.java
@@ -0,0 +1,24 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.demo.consumer;

+

+public class DemoConsumer {

+	

+	public static void main(String[] args) {

+	    com.alibaba.dubbo.container.Main.main(args);

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/ExitConsumer.java b/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/ExitConsumer.java
new file mode 100644
index 0000000..5b27cba
--- /dev/null
+++ b/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/ExitConsumer.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.demo.consumer;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+import com.alibaba.dubbo.demo.DemoService;

+

+public class ExitConsumer {

+	

+	public static void main(String[] args) {

+	    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"META-INF/spring/dubbo-demo-consumer.xml"});

+	    context.start();

+	    DemoService demoService = (DemoService)context.getBean("demoService");

+        String hello = demoService.sayHello("world");

+        System.out.println("demoService.sayHello result: " + hello);

+        System.out.println("Exit after once invoke!");

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-demo-consumer/src/test/resources/dubbo.properties b/dubbo-demo-consumer/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..5d04fe8
--- /dev/null
+++ b/dubbo-demo-consumer/src/test/resources/dubbo.properties
@@ -0,0 +1,25 @@
+##

+# Copyright 1999-2011 Alibaba Group.

+#  

+# 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.

+##

+dubbo.container=log4j,spring

+dubbo.application.name=demo-consumer

+dubbo.application.owner=

+dubbo.registry.address=multicast://224.5.6.7:1234

+#dubbo.registry.address=zookeeper://127.0.0.1:2181

+#dubbo.registry.address=redis://127.0.0.1:6379

+#dubbo.registry.address=dubbo://127.0.0.1:9090

+dubbo.monitor.protocol=registry

+#dubbo.log4j.file=logs/dubbo-demo-consumer.log

+#dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-demo-consumer/src/test/resources/log4j.xml b/dubbo-demo-consumer/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-demo-consumer/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">

+	<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

+		<layout class="org.apache.log4j.PatternLayout">

+			<param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />

+		</layout>

+	</appender>

+	<root>

+		<level value="INFO" />

+		<appender-ref ref="CONSOLE" />

+	</root>

+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-demo-provider/pom.xml b/dubbo-demo-provider/pom.xml
new file mode 100644
index 0000000..ad15d0c
--- /dev/null
+++ b/dubbo-demo-provider/pom.xml
@@ -0,0 +1,113 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-demo-provider</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Demo Provider Module</name>
+	<description>The demo provider module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>false</skip_maven_deploy>

+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-demo</artifactId>

+			<version>${project.parent.version}</version>
+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>org.mortbay.jetty</groupId>

+			<artifactId>jetty</artifactId>

+		</dependency>

+		<dependency>

+		    <groupId>org.apache.zookeeper</groupId>

+		    <artifactId>zookeeper</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.mina</groupId>

+			<artifactId>mina-core</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>org.glassfish.grizzly</groupId>

+			<artifactId>grizzly-core</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.httpcomponents</groupId>

+			<artifactId>httpclient</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>com.caucho</groupId>

+			<artifactId>hessian</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>fastjson</artifactId>

+		</dependency>
+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<artifactId>maven-dependency-plugin</artifactId>

+				<executions>

+					<execution>

+						<id>unpack</id>

+						<phase>package</phase>

+						<goals>

+							<goal>unpack</goal>

+						</goals>

+						<configuration>

+							<artifactItems>

+								<artifactItem>

+									<groupId>com.alibaba</groupId>

+									<artifactId>dubbo</artifactId>

+									<version>${project.parent.version}</version>

+									<outputDirectory>${project.build.directory}/dubbo</outputDirectory>

+									<includes>META-INF/assembly/**</includes>

+								</artifactItem>

+							</artifactItems>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+			<plugin>

+				<artifactId>maven-assembly-plugin</artifactId>

+                <configuration>

+                    <descriptor>src/main/assembly/assembly.xml</descriptor>

+                </configuration>

+                <executions>

+					<execution>

+						<id>make-assembly</id>

+						<phase>package</phase>

+						<goals>

+							<goal>single</goal>

+						</goals>

+					</execution>

+				</executions>

+            </plugin>

+		</plugins>

+	</build>
+</project>
\ No newline at end of file
diff --git a/dubbo-demo-provider/src/main/assembly/assembly.xml b/dubbo-demo-provider/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..25235d9
--- /dev/null
+++ b/dubbo-demo-provider/src/main/assembly/assembly.xml
@@ -0,0 +1,39 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<assembly>

+	<id>assembly</id>

+	<formats>

+		<format>tar.gz</format>

+	</formats>

+	<includeBaseDirectory>true</includeBaseDirectory>

+	<fileSets>

+		<fileSet>

+			<directory>${project.build.directory}/dubbo/META-INF/assembly/bin</directory>

+			<outputDirectory>bin</outputDirectory>

+			<fileMode>0755</fileMode>

+		</fileSet>

+		<fileSet>

+			<directory>src/main/assembly/conf</directory>

+			<outputDirectory>conf</outputDirectory>

+			<fileMode>0644</fileMode>

+		</fileSet>

+	</fileSets>

+	<dependencySets>

+		<dependencySet>

+			<outputDirectory>lib</outputDirectory>

+		</dependencySet>

+	</dependencySets>

+</assembly>
\ No newline at end of file
diff --git a/dubbo-demo-provider/src/main/assembly/conf/dubbo.properties b/dubbo-demo-provider/src/main/assembly/conf/dubbo.properties
new file mode 100644
index 0000000..f107516
--- /dev/null
+++ b/dubbo-demo-provider/src/main/assembly/conf/dubbo.properties
@@ -0,0 +1,27 @@
+##

+# Copyright 1999-2011 Alibaba Group.

+#  

+# 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.

+##

+dubbo.container=log4j,spring

+dubbo.application.name=demo-provider

+dubbo.application.owner=

+dubbo.registry.address=multicast://224.5.6.7:1234

+#dubbo.registry.address=zookeeper://127.0.0.1:2181

+#dubbo.registry.address=redis://127.0.0.1:6379

+#dubbo.registry.address=dubbo://127.0.0.1:9090

+dubbo.monitor.protocol=registry

+dubbo.protocol.name=dubbo

+dubbo.protocol.port=20880

+dubbo.log4j.file=logs/dubbo-demo-provider.log

+dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-demo-provider/src/main/java/com/alibaba/dubbo/demo/provider/DemoServiceImpl.java b/dubbo-demo-provider/src/main/java/com/alibaba/dubbo/demo/provider/DemoServiceImpl.java
new file mode 100644
index 0000000..346ab60
--- /dev/null
+++ b/dubbo-demo-provider/src/main/java/com/alibaba/dubbo/demo/provider/DemoServiceImpl.java
@@ -0,0 +1,31 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.demo.provider;

+

+import java.text.SimpleDateFormat;

+import java.util.Date;

+

+import com.alibaba.dubbo.demo.DemoService;

+import com.alibaba.dubbo.rpc.RpcContext;

+

+public class DemoServiceImpl implements DemoService {

+

+    public String sayHello(String name) {

+        System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());

+        return "Hello " + name + ", response form provider: " + RpcContext.getContext().getLocalAddress();

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-demo-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml b/dubbo-demo-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml
new file mode 100644
index 0000000..ae5c35d
--- /dev/null
+++ b/dubbo-demo-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+	

+	<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />

+	

+	<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" loadbalance="roundrobin" />

+	

+</beans>
\ No newline at end of file
diff --git a/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/DemoProvider.java b/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/DemoProvider.java
new file mode 100644
index 0000000..31850ef
--- /dev/null
+++ b/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/DemoProvider.java
@@ -0,0 +1,24 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.demo.provider;

+

+public class DemoProvider {

+

+	public static void main(String[] args) {

+        com.alibaba.dubbo.container.Main.main(args);

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/ExitProvider.java b/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/ExitProvider.java
new file mode 100644
index 0000000..52a0ece
--- /dev/null
+++ b/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/ExitProvider.java
@@ -0,0 +1,29 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.demo.provider;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+public class ExitProvider {

+	

+	public static void main(String[] args) throws Exception {

+	    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"META-INF/spring/dubbo-demo-provider.xml"});

+	    context.start();

+	    System.out.print("Press any key to exit: ");

+        System.in.read();

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-demo-provider/src/test/resources/dubbo.properties b/dubbo-demo-provider/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..9dee428
--- /dev/null
+++ b/dubbo-demo-provider/src/test/resources/dubbo.properties
@@ -0,0 +1,27 @@
+##

+# Copyright 1999-2011 Alibaba Group.

+#  

+# 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.

+##

+dubbo.container=log4j,spring

+dubbo.application.name=demo-provider

+dubbo.application.owner=william

+dubbo.registry.address=multicast://224.5.6.7:1234

+#dubbo.registry.address=zookeeper://127.0.0.1:2181

+#dubbo.registry.address=redis://127.0.0.1:6379

+#dubbo.registry.address=dubbo://127.0.0.1:9090

+dubbo.monitor.protocol=registry

+dubbo.protocol.name=dubbo

+dubbo.protocol.port=-1

+#dubbo.log4j.file=logs/dubbo-demo-consumer.log

+#dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-demo-provider/src/test/resources/log4j.xml b/dubbo-demo-provider/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-demo-provider/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">

+	<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

+		<layout class="org.apache.log4j.PatternLayout">

+			<param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />

+		</layout>

+	</appender>

+	<root>

+		<level value="INFO" />

+		<appender-ref ref="CONSOLE" />

+	</root>

+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-demo/pom.xml b/dubbo-demo/pom.xml
new file mode 100644
index 0000000..2b33fb3
--- /dev/null
+++ b/dubbo-demo/pom.xml
@@ -0,0 +1,31 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-demo</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Demo Module</name>
+	<description>The demo module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+</project>
\ No newline at end of file
diff --git a/dubbo-demo/src/main/java/com/alibaba/dubbo/demo/DemoService.java b/dubbo-demo/src/main/java/com/alibaba/dubbo/demo/DemoService.java
new file mode 100644
index 0000000..7dda95f
--- /dev/null
+++ b/dubbo-demo/src/main/java/com/alibaba/dubbo/demo/DemoService.java
@@ -0,0 +1,22 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.demo;

+

+public interface DemoService {

+

+	String sayHello(String name);

+

+}
\ No newline at end of file
diff --git a/dubbo-examples/pom.xml b/dubbo-examples/pom.xml
new file mode 100644
index 0000000..33d2196
--- /dev/null
+++ b/dubbo-examples/pom.xml
@@ -0,0 +1,126 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+	<modelVersion>4.0.0</modelVersion>

+	<parent>

+		<groupId>com.alibaba</groupId>

+		<artifactId>dubbo-parent</artifactId>

+		<version>2.1.1</version>

+	</parent>

+	<artifactId>dubbo-examples</artifactId>

+	<packaging>jar</packaging>

+	<name>Dubbo Examples Module</name>

+	<description>The examples module of dubbo project</description>

+	<properties>

+		<skip_maven_deploy>true</skip_maven_deploy>

+	</properties>

+	<dependencies>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-config</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-netty</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-mina</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-grizzly</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-p2p</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-http</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-default</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-injvm</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-rmi</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-hessian</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-registry-default</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-registry-multicast</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-registry-zookeeper</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-monitor-default</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>javax.validation</groupId>

+			<artifactId>validation-api</artifactId>

+		</dependency>

+		<dependency>

+			<groupId>org.hibernate</groupId>

+			<artifactId>hibernate-validator</artifactId>

+		</dependency>

+	</dependencies>

+

+    <build>

+        <resources>

+            <resource>

+                <directory>${basedir}/src/main/java</directory>

+                <excludes>

+                    <exclude>**/*.java</exclude>

+                </excludes>

+            </resource>

+            <resource>

+                <directory>${basedir}/src/main/resources</directory>

+            </resource>

+        </resources>

+    </build>

+

+</project>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheConsumer.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheConsumer.java
new file mode 100644
index 0000000..ec70d79
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheConsumer.java
@@ -0,0 +1,70 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.cache;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+import com.alibaba.dubbo.examples.cache.api.CacheService;

+

+/**

+ * CacheConsumer

+ * 

+ * @author william.liangf

+ */

+public class CacheConsumer {

+    

+    public static void main(String[] args) throws Exception {

+        String config = CacheConsumer.class.getPackage().getName().replace('.', '/') + "/cache-consumer.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        

+        CacheService cacheService = (CacheService)context.getBean("cacheService");

+

+        // 测试缓存生效,多次调用返回同样的结果。(服务器端自增长返回值)

+        String fix = null;

+        for (int i = 0; i < 5; i ++) {

+            String result = cacheService.findCache("0");

+            if (fix == null || fix.equals(result)) {

+                System.out.println("OK: " + result);

+            } else {

+                System.err.println("ERROR: " + result);

+            }

+            fix = result;

+            Thread.sleep(500);

+        }

+        

+        // LRU的缺省cache.size为1000,执行1001次,应有溢出

+        for (int n = 0; n < 1001; n ++) {

+            String pre = null;

+            for (int i = 0; i < 10; i ++) {

+                String result = cacheService.findCache(String.valueOf(n));

+                if (pre != null && ! pre.equals(result)) {

+                    System.err.println("ERROR: " + result);

+                }

+                pre = result;

+            }

+        }

+        

+        // 测试LRU有移除最开始的一个缓存项

+        String result = cacheService.findCache("0");

+        if (fix != null && ! fix.equals(result)) {

+            System.out.println("OK: " + result);

+        } else {

+            System.err.println("ERROR: " + result);

+        }

+    }

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheProvider.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheProvider.java
new file mode 100644
index 0000000..88dd11b
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheProvider.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.cache;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+/**

+ * CacheProvider

+ * 

+ * @author william.liangf

+ */

+public class CacheProvider {

+    

+    public static void main(String[] args) throws Exception {

+        String config = CacheProvider.class.getPackage().getName().replace('.', '/') + "/cache-provider.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        System.in.read();

+    }

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/api/CacheService.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/api/CacheService.java
new file mode 100644
index 0000000..90facfd
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/api/CacheService.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.cache.api;

+

+/**

+ * ValidationService

+ * 

+ * @author william.liangf

+ */

+public interface CacheService {

+

+    String findCache(String id);

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-consumer.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-consumer.xml
new file mode 100644
index 0000000..32dfb10
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="cache-consumer"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+	<dubbo:reference id="cacheService" interface="com.alibaba.dubbo.examples.cache.api.CacheService" cache="true" />

+

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-provider.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-provider.xml
new file mode 100644
index 0000000..1875559
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-provider.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="cache-provider"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+    <dubbo:protocol name="dubbo" port="20880" />

+    

+	<bean id="cacheService" class="com.alibaba.dubbo.examples.cache.impl.CacheServiceImpl" />

+	

+	<dubbo:service interface="com.alibaba.dubbo.examples.cache.api.CacheService" ref="cacheService" />

+	

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/impl/CacheServiceImpl.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/impl/CacheServiceImpl.java
new file mode 100644
index 0000000..5710e0f
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/cache/impl/CacheServiceImpl.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.cache.impl;

+

+import java.util.concurrent.atomic.AtomicInteger;

+

+import com.alibaba.dubbo.examples.cache.api.CacheService;

+

+/**

+ * ValidationServiceImpl

+ * 

+ * @author william.liangf

+ */

+public class CacheServiceImpl implements CacheService {

+    

+    private final AtomicInteger i = new AtomicInteger();

+

+    public String findCache(String id) {

+        return "request: " + id + ", response: " + i.getAndIncrement();

+    }

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackConsumer.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackConsumer.java
new file mode 100644
index 0000000..576762b
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackConsumer.java
@@ -0,0 +1,43 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.callback;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+import com.alibaba.dubbo.examples.callback.api.CallbackListener;

+import com.alibaba.dubbo.examples.callback.api.CallbackService;

+

+/**

+ * CallbackConsumer

+ * 

+ * @author william.liangf

+ */

+public class CallbackConsumer {

+

+    public static void main(String[] args) throws Exception {

+        String config = CallbackConsumer.class.getPackage().getName().replace('.', '/') + "/callback-consumer.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        CallbackService callbackService = (CallbackService) context.getBean("callbackService");

+        callbackService.addListener("foo.bar", new CallbackListener() {

+            public void changed(String msg) {

+                System.out.println("callback1:" + msg);

+            }

+        });

+        System.in.read();

+    }

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackProvider.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackProvider.java
new file mode 100644
index 0000000..a530d97
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackProvider.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.callback;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+/**

+ * MergeProvider

+ * 

+ * @author william.liangf

+ */

+public class CallbackProvider {

+    

+    public static void main(String[] args) throws Exception {

+        String config = CallbackProvider.class.getPackage().getName().replace('.', '/') + "/callback-provider.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        System.in.read();

+    }

+    

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackListener.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackListener.java
new file mode 100644
index 0000000..bc7d172
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackListener.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.callback.api;

+

+/**

+ * CallbackListener

+ * 

+ * @author william.liangf

+ */

+public interface CallbackListener {

+    

+    void changed(String msg);

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackService.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackService.java
new file mode 100644
index 0000000..93675aa
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackService.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.callback.api;

+

+/**

+ * CallbackService

+ * 

+ * @author william.liangf

+ */

+public interface CallbackService {

+    

+    void addListener(String key, CallbackListener listener);

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-consumer.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-consumer.xml
new file mode 100644
index 0000000..66cdaca
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="callback-consumer"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+	<dubbo:reference id="callbackService" interface="com.alibaba.dubbo.examples.callback.api.CallbackService" />

+

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-provider.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-provider.xml
new file mode 100644
index 0000000..7e90faa
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-provider.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="merge-provider"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+    <dubbo:protocol name="dubbo" port="20880" />

+    

+    <bean id="callbackService" class="com.alibaba.dubbo.examples.callback.impl.CallbackServiceImpl" />

+    

+	<dubbo:service interface="com.alibaba.dubbo.examples.callback.api.CallbackService" ref="callbackService" connections="1" callbacks="1000">

+		<dubbo:method name="addListener">

+			<dubbo:argument index="1" callback="true" />

+			<!--<dubbo:argument type="com.demo.CallbackListener" callback="true" />-->

+		</dubbo:method>

+	</dubbo:service>

+	

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/impl/CallbackServiceImpl.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/impl/CallbackServiceImpl.java
new file mode 100644
index 0000000..c904b33
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/callback/impl/CallbackServiceImpl.java
@@ -0,0 +1,67 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.callback.impl;

+

+import java.text.SimpleDateFormat;

+import java.util.Date;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import com.alibaba.dubbo.examples.callback.api.CallbackListener;

+import com.alibaba.dubbo.examples.callback.api.CallbackService;

+

+/**

+ * CallbackServiceImpl

+ * 

+ * @author william.liangf

+ */

+public class CallbackServiceImpl implements CallbackService {

+    

+   private final Map<String, CallbackListener> listeners = new ConcurrentHashMap<String, CallbackListener>();

+ 

+   public CallbackServiceImpl() {

+       Thread t = new Thread(new Runnable() {

+           public void run() {

+               while(true) {

+                   try {

+                       for(Map.Entry<String, CallbackListener> entry : listeners.entrySet()){

+                           try {

+                               entry.getValue().changed(getChanged(entry.getKey()));

+                           } catch (Throwable t) {

+                               listeners.remove(entry.getKey());

+                           }

+                       }

+                       Thread.sleep(5000); // 定时触发变更通知

+                   } catch (Throwable t) { // 防御容错

+                       t.printStackTrace();

+                   }

+               }

+           }

+       });

+       t.setDaemon(true);

+       t.start();

+   }

+ 

+   public void addListener(String key, CallbackListener listener) {

+       listeners.put(key, listener);

+       listener.changed(getChanged(key)); // 发送变更通知

+   }

+    

+   private String getChanged(String key) {

+       return "Changed: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

+   }

+

+}
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericConsumer.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericConsumer.java
new file mode 100644
index 0000000..0d95d5b
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericConsumer.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.generic;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+import com.alibaba.dubbo.examples.generic.api.IUserService;

+import com.alibaba.dubbo.examples.generic.api.IUserService.Params;

+import com.alibaba.dubbo.examples.generic.api.IUserService.User;

+

+/**

+ * GenericConsumer

+ * 

+ * @author chao.liuc

+ */

+public class GenericConsumer {

+

+    public static void main(String[] args) throws Exception {

+        String config = GenericConsumer.class.getPackage().getName().replace('.', '/') + "/generic-consumer.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        IUserService userservice = (IUserService) context.getBean("userservice");

+        User user = userservice.get(new Params("a=b"));

+        System.out.println(user);

+        System.in.read();

+    }

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericProvider.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericProvider.java
new file mode 100644
index 0000000..0fc38f9
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericProvider.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.generic;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+/**

+ * GenericProvider

+ * 

+ * @author chao.liuc

+ */

+public class GenericProvider {

+    

+    public static void main(String[] args) throws Exception {

+        String config = GenericProvider.class.getPackage().getName().replace('.', '/') + "/generic-provider.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        System.in.read();

+    }

+    

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IService.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IService.java
new file mode 100644
index 0000000..3d1f3e3
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IService.java
@@ -0,0 +1,5 @@
+package com.alibaba.dubbo.examples.generic.api;
+
+public interface IService <P, V> {
+    V get(P params);
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IUserService.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IUserService.java
new file mode 100644
index 0000000..09a855a
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IUserService.java
@@ -0,0 +1,62 @@
+/**
+ * Project: dubbo-examples
+ * 
+ * File Created at 2012-2-17
+ * $Id$
+ * 
+ * Copyright 1999-2100 Alibaba.com Corporation Limited.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Alibaba Company. ("Confidential Information").  You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Alibaba.com.
+ */
+package com.alibaba.dubbo.examples.generic.api;
+
+import java.io.Serializable;
+
+import com.alibaba.dubbo.examples.generic.api.IUserService.Params;
+import com.alibaba.dubbo.examples.generic.api.IUserService.User;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public interface IUserService extends IService<Params, User> {
+
+
+    public static class Params implements Serializable{
+        private static final long serialVersionUID = 1L;
+
+        public Params(String query) {
+        }
+    }
+    public static class User implements Serializable{
+        private static final long serialVersionUID = 1L;
+        public User(int id, String name) {
+            super();
+            this.id = id;
+            this.name = name;
+        }
+        private int id;
+        private String name;
+        public int getId() {
+            return id;
+        }
+        public void setId(int id) {
+            this.id = id;
+        }
+        public String getName() {
+            return name;
+        }
+        public void setName(String name) {
+            this.name = name;
+        }
+        @Override
+        public String toString() {
+            return "User [id=" + id + ", name=" + name + "]";
+        }
+    }
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-consumer.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-consumer.xml
new file mode 100644
index 0000000..4f4b387
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="generic-consumer"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+	<dubbo:reference id="userservice" interface="com.alibaba.dubbo.examples.generic.api.IUserService" />

+

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-provider.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-provider.xml
new file mode 100644
index 0000000..efddb37
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-provider.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="generic-generic"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+    <dubbo:protocol name="dubbo" port="20880" />

+    

+    <bean id="userserviceimpl" class="com.alibaba.dubbo.examples.generic.impl.UserServiceImpl" />

+    

+	<dubbo:service interface="com.alibaba.dubbo.examples.generic.api.IUserService" ref="userserviceimpl" >

+	</dubbo:service>

+	

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/impl/UserServiceImpl.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/impl/UserServiceImpl.java
new file mode 100644
index 0000000..31329b1
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/generic/impl/UserServiceImpl.java
@@ -0,0 +1,29 @@
+/**
+ * Project: dubbo-examples
+ * 
+ * File Created at 2012-2-17
+ * $Id$
+ * 
+ * Copyright 1999-2100 Alibaba.com Corporation Limited.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Alibaba Company. ("Confidential Information").  You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Alibaba.com.
+ */
+package com.alibaba.dubbo.examples.generic.impl;
+
+import com.alibaba.dubbo.examples.generic.api.IUserService;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class UserServiceImpl implements IUserService {
+
+    public User get(Params params) {
+        return new User(1, "charles");
+    }
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer.java
new file mode 100644
index 0000000..e67024d
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.merge;

+

+import java.util.List;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+import com.alibaba.dubbo.examples.merge.api.MergeService;

+

+/**

+ * MergeConsumer

+ * 

+ * @author william.liangf

+ */

+public class MergeConsumer {

+    

+    public static void main(String[] args) throws Exception {

+        String config = MergeConsumer.class.getPackage().getName().replace('.', '/') + "/merge-consumer.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        MergeService mergeService = (MergeService)context.getBean("mergeService");

+        for (int i = 0; i < Integer.MAX_VALUE; i ++) {

+            try {

+                List<String> result = mergeService.mergeResult();

+                System.out.println("(" + i + ") " + result);

+                Thread.sleep(1000);

+            } catch (Exception e) {

+                e.printStackTrace();

+            }

+        }

+    }

+    

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer2.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer2.java
new file mode 100644
index 0000000..ca5016a
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer2.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.merge;

+

+import java.util.List;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+import com.alibaba.dubbo.examples.merge.api.MergeService;

+

+/**

+ * MergeConsumer2

+ * 

+ * @author william.liangf

+ */

+public class MergeConsumer2 {

+    

+    public static void main(String[] args) throws Exception {

+        String config = MergeConsumer2.class.getPackage().getName().replace('.', '/') + "/merge-consumer2.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        MergeService mergeService = (MergeService)context.getBean("mergeService");

+        for (int i = 0; i < Integer.MAX_VALUE; i ++) {

+            try {

+                List<String> result = mergeService.mergeResult();

+                System.out.println("(" + i + ") " + result);

+                Thread.sleep(1000);

+            } catch (Exception e) {

+                e.printStackTrace();

+            }

+        }

+    }

+    

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider.java
new file mode 100644
index 0000000..ee49b69
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.merge;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+/**

+ * MergeProvider

+ * 

+ * @author william.liangf

+ */

+public class MergeProvider {

+    

+    public static void main(String[] args) throws Exception {

+        String config = MergeProvider.class.getPackage().getName().replace('.', '/') + "/merge-provider.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        System.in.read();

+    }

+    

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider2.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider2.java
new file mode 100644
index 0000000..bf1bd80
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider2.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.merge;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+/**

+ * MergeProvider2

+ * 

+ * @author william.liangf

+ */

+public class MergeProvider2 {

+    

+    public static void main(String[] args) throws Exception {

+        String config = MergeProvider2.class.getPackage().getName().replace('.', '/') + "/merge-provider2.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        System.in.read();

+    }

+    

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/api/MergeService.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/api/MergeService.java
new file mode 100644
index 0000000..eb8197f
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/api/MergeService.java
@@ -0,0 +1,29 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.merge.api;

+

+import java.util.List;

+

+/**

+ * MergeService

+ * 

+ * @author william.liangf

+ */

+public interface MergeService {

+    

+    List<String> mergeResult();

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl.java
new file mode 100644
index 0000000..c1e5611
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.merge.impl;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.examples.merge.api.MergeService;

+

+/**

+ * MenuServiceImpl

+ * 

+ * @author william.liangf

+ */

+public class MergeServiceImpl implements MergeService {

+    

+    public List<String> mergeResult() {

+        List<String> menus = new ArrayList<String>();

+        menus.add("group-1.1");

+        menus.add("group-1.2");

+        return menus;

+    }

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl2.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl2.java
new file mode 100644
index 0000000..a8f2c06
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl2.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.merge.impl;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.examples.merge.api.MergeService;

+

+/**

+ * MenuServiceImpl

+ * 

+ * @author william.liangf

+ */

+public class MergeServiceImpl2 implements MergeService {

+    

+    public List<String> mergeResult() {

+        List<String> menus = new ArrayList<String>();

+        menus.add("group-2.1");

+        menus.add("group-2.2");

+        return menus;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl3.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl3.java
new file mode 100644
index 0000000..3185da8
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl3.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.merge.impl;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.examples.merge.api.MergeService;

+

+/**

+ * MenuServiceImpl

+ * 

+ * @author william.liangf

+ */

+public class MergeServiceImpl3 implements MergeService {

+    

+    public List<String> mergeResult() {

+        List<String> menus = new ArrayList<String>();

+        menus.add("group-3.1");

+        menus.add("group-3.2");

+        return menus;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer.xml
new file mode 100644
index 0000000..05f7339
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="merge-consumer"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+	<dubbo:reference id="mergeService" interface="com.alibaba.dubbo.examples.merge.api.MergeService" group="*" />

+

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer2.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer2.xml
new file mode 100644
index 0000000..d9673db
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer2.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="merge-consumer2"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+	<dubbo:reference id="mergeService" interface="com.alibaba.dubbo.examples.merge.api.MergeService" group="merge2,merge3" />

+

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider.xml
new file mode 100644
index 0000000..bdcaa71
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="merge-provider"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+    <dubbo:protocol name="dubbo" port="20880" />

+    

+	<bean id="mergeService" class="com.alibaba.dubbo.examples.merge.impl.MergeServiceImpl" />

+	

+	<dubbo:service group="merge" interface="com.alibaba.dubbo.examples.merge.api.MergeService" ref="mergeService" />

+	

+	<bean id="mergeService2" class="com.alibaba.dubbo.examples.merge.impl.MergeServiceImpl2" />

+	

+	<dubbo:service group="merge2" interface="com.alibaba.dubbo.examples.merge.api.MergeService" ref="mergeService2" />

+	

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider2.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider2.xml
new file mode 100644
index 0000000..b525e24
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="merge-provider2"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+    <dubbo:protocol name="dubbo" port="20881" />

+    

+	<bean id="mergeService3" class="com.alibaba.dubbo.examples.merge.impl.MergeServiceImpl3" />

+	

+	<dubbo:service group="merge3" interface="com.alibaba.dubbo.examples.merge.api.MergeService" ref="mergeService3" />

+	

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartBeatExchangeHandler.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartBeatExchangeHandler.java
new file mode 100644
index 0000000..2f0d98a
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartBeatExchangeHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *    
+ *   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.dubbo.examples.remoting.heartbeat;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartBeatExchangeHandler extends HeaderExchangeHandler {
+
+    private AtomicInteger heartBeatCounter = new AtomicInteger( 0 );
+    
+    public HeartBeatExchangeHandler( ExchangeHandler handler ) {
+        super( handler );
+    }
+
+    @Override
+    public void received( Channel channel, Object message ) throws RemotingException {
+        if ( message instanceof Request ) {
+            Request req = ( Request ) message;
+            if ( req.isHeartbeat() ) {
+                heartBeatCounter.incrementAndGet();
+                channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
+                Response res = new Response( req.getId(), req.getVersion() );
+                res.setEvent( req.getData() == null ? null : req.getData().toString() );
+                channel.send( res );
+            }
+        } else {
+            super.received( channel, message );
+        }
+    }
+    
+    public int getHeartBeatCount() {
+        return heartBeatCounter.get();
+    }
+    
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatClient.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatClient.java
new file mode 100644
index 0000000..b2f0840
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatClient.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.examples.remoting.heartbeat;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Transporters;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartbeatClient {
+
+    private static final URL serverUrl = URL.valueOf(
+            new StringBuilder( 32 )
+                    .append( "netty://" )
+                    .append( NetUtils.getLocalHost() )
+                    .append( ":9999" ).toString() )
+            .addParameter( Constants.CODEC_KEY, "exchange" );
+
+    private static final ExchangeHandler handler = new ExchangeHandlerAdapter() {
+
+    };
+
+    private static ExchangeServer exchangeServer;
+    
+    private static volatile boolean serverStarted = false;
+    
+    public static void main( String[] args ) throws Exception {
+        
+        final HeartBeatExchangeHandler serverHandler = new HeartBeatExchangeHandler( handler );
+        
+        Thread serverThread = new Thread( new Runnable() {
+
+            public void run() {
+                try {
+                    exchangeServer = new HeaderExchangeServer(
+                            Transporters.bind( serverUrl, serverHandler ) );
+                    serverStarted = true;
+                } catch ( Exception e ) {
+                    e.printStackTrace();
+                }
+            }
+        } );
+        
+        serverThread.setDaemon( true );
+        serverThread.start();
+        
+        while ( ! serverStarted ) {
+            Thread.sleep( 1000 );
+        }
+        
+        URL url = serverUrl.addParameter( Constants.HEARTBEAT_KEY, 1000 );
+
+        HeartBeatExchangeHandler clientHandler = new HeartBeatExchangeHandler( handler );
+        ExchangeClient exchangeClient = new HeaderExchangeClient(
+                Transporters.connect( url, clientHandler ) );
+        
+        for( int i = 0; i < 10; i++ ) {
+            Thread.sleep( 1000 );
+            System.out.print( "." );
+        }
+
+        System.out.println();
+
+        if ( serverHandler.getHeartBeatCount() > 0 ) {
+            System.out.printf( "Server receives %d heartbeats",
+                               serverHandler.getHeartBeatCount() );
+        } else {
+            throw new Exception( "Client heartbeat does not work." );
+        }
+
+        exchangeClient.close();
+        exchangeServer.close();
+
+    }
+
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatConsumer.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatConsumer.java
new file mode 100644
index 0000000..f520d25
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatConsumer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.examples.remoting.heartbeat;
+
+import com.alibaba.dubbo.examples.remoting.heartbeat.api.HelloService;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartbeatConsumer {
+
+    public static void main(String[] args) throws Exception {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
+                HeartbeatConsumer.class.getPackage().getName().replace('.', '/') + "/heartbeat-consumer.xml");
+        context.start();
+        HelloService hello = (HelloService) context.getBean("helloService");
+        for (int i = 0; i < Integer.MAX_VALUE; i++) {
+            System.out.println(hello.sayHello("kimi-" + i));
+            Thread.sleep(10000);
+        }
+    }
+
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatProvider.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatProvider.java
new file mode 100644
index 0000000..07cc7ec
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.examples.remoting.heartbeat;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartbeatProvider {
+
+    public static void main(String[] args) throws Exception {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( 
+                HeartbeatProvider.class.getPackage().getName().replace( '.', '/' ) + "/heartbeat-provider.xml" );
+        context.start();
+        System.in.read();
+    }
+
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatServer.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatServer.java
new file mode 100644
index 0000000..3c56f31
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/HeartbeatServer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.examples.remoting.heartbeat;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Transporters;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartbeatServer {
+
+    private static final URL clientUrl = URL.valueOf(
+            new StringBuilder( 32 )
+                    .append( "netty://" )
+                    .append( NetUtils.getLocalHost() )
+                    .append( ":9999" ).toString() )
+            .addParameter( Constants.CODEC_KEY, "exchange" );
+
+    private static final ExchangeHandler handler = new ExchangeHandlerAdapter() {
+
+    };
+
+    private static ExchangeServer exchangeServer;
+
+    private static volatile boolean serverStarted = false;
+
+    public static void main( String[] args ) throws Exception {
+
+        final HeartBeatExchangeHandler serverHandler = new HeartBeatExchangeHandler( handler );
+
+        Thread serverThread = new Thread( new Runnable() {
+
+            public void run() {
+                try {
+                    exchangeServer = new HeaderExchangeServer(
+                            Transporters.bind(
+                                    clientUrl.addParameter( Constants.HEARTBEAT_KEY, 1000 ),
+                                    serverHandler ) );
+                    serverStarted = true;
+                } catch ( Exception e ) {
+                    e.printStackTrace();
+                }
+            }
+        } );
+
+        serverThread.setDaemon( true );
+        serverThread.start();
+
+        while ( !serverStarted ) {
+            Thread.sleep( 1000 );
+        }
+
+        HeartBeatExchangeHandler clientHandler = new HeartBeatExchangeHandler( handler );
+        ExchangeClient exchangeClient = new HeaderExchangeClient(
+                Transporters.connect( clientUrl, clientHandler ) );
+
+        for ( int i = 0; i < 10; i++ ) {
+            Thread.sleep( 1000 );
+            System.out.print( "." );
+        }
+
+        System.out.println();
+
+        if ( clientHandler.getHeartBeatCount() > 0 ) {
+            System.out.printf( "Client receives %d heartbeats",
+                               clientHandler.getHeartBeatCount() );
+        } else {
+            throw new Exception( "Server heartbeat does not work." );
+        }
+
+        exchangeClient.close();
+        exchangeServer.close();
+
+    }
+
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/api/HelloService.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/api/HelloService.java
new file mode 100644
index 0000000..e2a96f8
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/api/HelloService.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *    
+ *   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.dubbo.examples.remoting.heartbeat.api;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public interface HelloService {
+    
+    public String sayHello(String name);
+
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/heartbeat-consumer.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/heartbeat-consumer.xml
new file mode 100644
index 0000000..6d293bc
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/heartbeat-consumer.xml
@@ -0,0 +1,30 @@
+<!--
+  - Copyright 1999-2012 Alibaba Group.
+  -
+  -   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.
+  -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+    <dubbo:application name="heartbeat-consumer"/>
+
+    <!--<dubbo:registry address="127.0.0.1:9090"/>-->
+
+    <dubbo:reference id="helloService" interface="com.alibaba.dubbo.examples.remoting.heartbeat.api.HelloService" url="dubbo://127.0.0.1:20880">
+        <dubbo:parameter key="heartbeat" value="3000" />
+    </dubbo:reference>
+
+</beans>
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/heartbeat-provider.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/heartbeat-provider.xml
new file mode 100644
index 0000000..49577bd
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/heartbeat-provider.xml
@@ -0,0 +1,34 @@
+<!--
+  - Copyright 1999-2012 Alibaba Group.
+  -
+  -   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.
+  -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+    <dubbo:application name="heartbeat-provider"/>
+
+    <!--<dubbo:registry address="127.0.0.1:9090"/>-->
+
+    <dubbo:protocol name="dubbo" port="20880" />
+
+    <bean id="helloService" class="com.alibaba.dubbo.examples.remoting.heartbeat.impl.HelloServiceImpl"/>
+
+    <dubbo:service registry="N/A" interface="com.alibaba.dubbo.examples.remoting.heartbeat.api.HelloService" ref="helloService">
+        <dubbo:parameter key="heartbeat" value="3000" />
+    </dubbo:service>
+
+</beans>
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/impl/HelloServiceImpl.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/impl/HelloServiceImpl.java
new file mode 100644
index 0000000..66d15b4
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/remoting/heartbeat/impl/HelloServiceImpl.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.examples.remoting.heartbeat.impl;
+
+import com.alibaba.dubbo.examples.remoting.heartbeat.api.HelloService;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HelloServiceImpl implements HelloService {
+
+    public String sayHello(String name) {
+        return new StringBuilder(32).append("Hello, ").append(name).append("!").toString();
+    }
+}
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationConsumer.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationConsumer.java
new file mode 100644
index 0000000..ed10c4b
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationConsumer.java
@@ -0,0 +1,80 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.validation;

+

+import java.util.Date;

+import java.util.Set;

+

+import javax.validation.ConstraintViolation;

+import javax.validation.ConstraintViolationException;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+import com.alibaba.dubbo.examples.validation.api.ValidationParameter;

+import com.alibaba.dubbo.examples.validation.api.ValidationService;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * ValidationConsumer

+ * 

+ * @author william.liangf

+ */

+public class ValidationConsumer {

+    

+    public static void main(String[] args) throws Exception {

+        String config = ValidationConsumer.class.getPackage().getName().replace('.', '/') + "/validation-consumer.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        

+        ValidationService validationService = (ValidationService)context.getBean("validationService");

+        

+        // Save OK

+        ValidationParameter parameter = new ValidationParameter();

+        parameter.setName("liangfei");

+        parameter.setEmail("liangfei@liang.fei");

+        parameter.setAge(50);

+        parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));

+        parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));

+        validationService.save(parameter);

+        System.out.println("Validation Save OK");

+        

+        // Save Error

+        try {

+            parameter = new ValidationParameter();

+            validationService.save(parameter);

+            System.err.println("Validation Save ERROR");

+        } catch (RpcException e) {

+            ConstraintViolationException ve = (ConstraintViolationException)e.getCause();

+            Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();

+            System.out.println(violations);

+        }

+        

+        // Delete OK

+        validationService.delete(2);

+        System.out.println("Validation Delete OK");

+        

+        // Delete Error

+        try {

+            validationService.delete(0);

+            System.err.println("Validation Delete ERROR");

+        } catch (RpcException e) {

+            ConstraintViolationException ve = (ConstraintViolationException)e.getCause();

+            Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();

+            System.out.println(violations);

+        }

+    }

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationProvider.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationProvider.java
new file mode 100644
index 0000000..a20f247
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationProvider.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.validation;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

+/**

+ * ValidationProvider

+ * 

+ * @author william.liangf

+ */

+public class ValidationProvider {

+    

+    public static void main(String[] args) throws Exception {

+        String config = ValidationProvider.class.getPackage().getName().replace('.', '/') + "/validation-provider.xml";

+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);

+        context.start();

+        System.in.read();

+    }

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationParameter.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationParameter.java
new file mode 100644
index 0000000..5f8a5ab
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationParameter.java
@@ -0,0 +1,96 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.validation.api;

+

+import java.io.Serializable;

+import java.util.Date;

+

+import javax.validation.constraints.Future;

+import javax.validation.constraints.Max;

+import javax.validation.constraints.Min;

+import javax.validation.constraints.NotNull;

+import javax.validation.constraints.Past;

+import javax.validation.constraints.Pattern;

+import javax.validation.constraints.Size;

+

+/**

+ * ValidationParameter

+ * 

+ * @author william.liangf

+ */

+public class ValidationParameter implements Serializable {

+    

+    private static final long serialVersionUID = 7158911668568000392L;

+

+    @NotNull // 不允许为空

+    @Size(min = 1, max = 20) // 长度或大小范围

+    private String name;

+

+    @NotNull(groups = ValidationService.Save.class) // 保存时不允许为空,更新时允许为空 ,表示不更新该字段

+    @Pattern(regexp = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$")

+    private String email;

+

+    @Min(18) // 最小值

+    @Max(100) // 最大值

+    private int age;

+

+    @Past // 必须为一个过去的时间

+    private Date loginDate;

+

+    @Future // 必须为一个未来的时间

+    private Date expiryDate;

+

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        this.name = name;

+    }

+

+    public String getEmail() {

+        return email;

+    }

+

+    public void setEmail(String email) {

+        this.email = email;

+    }

+

+    public int getAge() {

+        return age;

+    }

+

+    public void setAge(int age) {

+        this.age = age;

+    }

+

+    public Date getLoginDate() {

+        return loginDate;

+    }

+

+    public void setLoginDate(Date loginDate) {

+        this.loginDate = loginDate;

+    }

+

+    public Date getExpiryDate() {

+        return expiryDate;

+    }

+

+    public void setExpiryDate(Date expiryDate) {

+        this.expiryDate = expiryDate;

+    }

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationService.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationService.java
new file mode 100644
index 0000000..a9c8829
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationService.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.validation.api;

+

+import javax.validation.constraints.Min;

+

+

+/**

+ * ValidationService

+ * 

+ * @author william.liangf

+ */

+public interface ValidationService { // 缺省可按服务接口区分验证场景,如:@NotNull(groups = ValidationService.class)

+    

+    @interface Save{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Save.class),可选

+    void save(ValidationParameter parameter);

+

+    @interface Update{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Update.class),可选

+    void update(ValidationParameter parameter);

+    

+    void delete(@Min(1) long id);

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/impl/ValidationServiceImpl.java b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/impl/ValidationServiceImpl.java
new file mode 100644
index 0000000..ca0f856
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/impl/ValidationServiceImpl.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.examples.validation.impl;

+

+import com.alibaba.dubbo.examples.validation.api.ValidationParameter;

+import com.alibaba.dubbo.examples.validation.api.ValidationService;

+

+/**

+ * ValidationServiceImpl

+ * 

+ * @author william.liangf

+ */

+public class ValidationServiceImpl implements ValidationService {

+

+    public void save(ValidationParameter parameter) {

+    }

+

+    public void update(ValidationParameter parameter) {

+    }

+

+    public void delete(long id) {

+    }

+

+}

diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-consumer.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-consumer.xml
new file mode 100644
index 0000000..0e437e6
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="validation-consumer"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+	<dubbo:reference id="validationService" interface="com.alibaba.dubbo.examples.validation.api.ValidationService" validation="true" />

+

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-provider.xml b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-provider.xml
new file mode 100644
index 0000000..cf92eb5
--- /dev/null
+++ b/dubbo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-provider.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+    <dubbo:application name="validation-provider"  />

+    

+    <dubbo:registry address="multicast://224.5.6.7:1234" />

+    

+    <dubbo:protocol name="dubbo" port="20880" />

+    

+	<bean id="validationService" class="com.alibaba.dubbo.examples.validation.impl.ValidationServiceImpl" />

+	

+	<dubbo:service interface="com.alibaba.dubbo.examples.validation.api.ValidationService" ref="validationService" validation="true" />

+	

+</beans>
\ No newline at end of file
diff --git a/dubbo-examples/src/main/resources/log4j.xml b/dubbo-examples/src/main/resources/log4j.xml
new file mode 100644
index 0000000..69ed180
--- /dev/null
+++ b/dubbo-examples/src/main/resources/log4j.xml
@@ -0,0 +1,27 @@
+<!--
+  - Copyright 1999-2012 Alibaba Group.
+  -
+  -   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.
+  -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+    <appender name="DUBBO" class="com.alibaba.dubbo.common.utils.DubboAppender">
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n"/>
+        </layout>
+    </appender>
+    <root>
+        <level value="INFO"/>
+        <appender-ref ref="DUBBO"/>
+    </root>
+</log4j:configuration>
diff --git a/dubbo-monitor-default/pom.xml b/dubbo-monitor-default/pom.xml
new file mode 100644
index 0000000..a64e83f
--- /dev/null
+++ b/dubbo-monitor-default/pom.xml
@@ -0,0 +1,50 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-monitor-default</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Default Monitor Module</name>
+	<description>The default monitor module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-monitor</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-default</artifactId>

+			<version>${project.parent.version}</version>

+			<scope>test</scope>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-netty</artifactId>

+			<version>${project.parent.version}</version>

+			<scope>test</scope>

+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.java b/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.java
new file mode 100644
index 0000000..6be72b2
--- /dev/null
+++ b/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.java
@@ -0,0 +1,201 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.dubbo;

+

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.atomic.AtomicReference;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.monitor.Monitor;

+import com.alibaba.dubbo.monitor.MonitorService;

+import com.alibaba.dubbo.rpc.Invoker;

+

+/**

+ * DubboMonitor

+ * 

+ * @author william.liangf

+ */

+public class DubboMonitor implements Monitor {

+    

+    private static final Logger logger = LoggerFactory.getLogger(DubboMonitor.class);

+    

+    private static final int LENGTH = 10;

+    

+    // 定时任务执行器

+    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3, new NamedThreadFactory("DubboMonitorSendTimer", true));

+

+    // 统计信息收集定时器

+    private final ScheduledFuture<?> sendFuture;

+    

+    private final Invoker<MonitorService> monitorInvoker;

+

+    private final MonitorService monitorService;

+

+    private final long monitorInterval;

+    

+    private final ConcurrentMap<Statistics, AtomicReference<long[]>> statisticsMap = new ConcurrentHashMap<Statistics, AtomicReference<long[]>>();

+

+    public DubboMonitor(Invoker<MonitorService> monitorInvoker, MonitorService monitorService) {

+        this.monitorInvoker = monitorInvoker;

+        this.monitorService = monitorService;

+        this.monitorInterval = monitorInvoker.getUrl().getPositiveParameter("interval", 60000);

+        // 启动统计信息收集定时器

+        sendFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {

+            public void run() {

+                // 收集统计信息

+                try {

+                    send();

+                } catch (Throwable t) { // 防御性容错

+                    logger.error("Unexpected error occur at send statistic, cause: " + t.getMessage(), t);

+                }

+            }

+        }, monitorInterval, monitorInterval, TimeUnit.MILLISECONDS);

+    }

+    

+    public void send() {

+        if (logger.isInfoEnabled()) {

+            logger.info("Send statistics to monitor " + getUrl());

+        }

+        String timestamp = String.valueOf(System.currentTimeMillis());

+        for (Map.Entry<Statistics, AtomicReference<long[]>> entry : statisticsMap.entrySet()) {

+            // 获取已统计数据

+            Statistics statistics = entry.getKey();

+            AtomicReference<long[]> reference = entry.getValue();

+            long[] numbers = reference.get();

+            long success = numbers[0];

+            long failure = numbers[1];

+            long input = numbers[2];

+            long output = numbers[3];

+            long elapsed = numbers[4];

+            long concurrent = numbers[5];

+            long maxInput = numbers[6];

+            long maxOutput = numbers[7];

+            long maxElapsed = numbers[8];

+            long maxConcurrent = numbers[9];

+             

+            // 发送汇总信息

+            URL url = statistics.getUrl()

+                    .addParameters(MonitorService.TIMESTAMP, timestamp,

+                            MonitorService.SUCCESS, String.valueOf(success),

+                            MonitorService.FAILURE, String.valueOf(failure), 

+                            MonitorService.INPUT, String.valueOf(input), 

+                            MonitorService.OUTPUT, String.valueOf(output),

+                            MonitorService.ELAPSED, String.valueOf(elapsed),

+                            MonitorService.CONCURRENT, String.valueOf(concurrent),

+                            MonitorService.MAX_INPUT, String.valueOf(maxInput),

+                            MonitorService.MAX_OUTPUT, String.valueOf(maxOutput),

+                            MonitorService.MAX_ELAPSED, String.valueOf(maxElapsed),

+                            MonitorService.MAX_CONCURRENT, String.valueOf(maxConcurrent)

+                            );

+            monitorService.count(url);

+            

+            // 减掉已统计数据

+            long[] current;

+            long[] update = new long[LENGTH];

+            do {

+                current = reference.get();

+                if (current == null) {

+                    update[0] = 0;

+                    update[1] = 0;

+                    update[2] = 0;

+                    update[3] = 0;

+                    update[4] = 0;

+                    update[5] = 0;

+                } else {

+                    update[0] = current[0] - success;

+                    update[1] = current[1] - failure;

+                    update[2] = current[2] - input;

+                    update[3] = current[3] - output;

+                    update[4] = current[4] - elapsed;

+                    update[5] = current[5] - concurrent;

+                }

+            } while (! reference.compareAndSet(current, update));

+        }

+    }

+    

+    public void count(URL url) {

+        // 读写统计变量

+        int success = url.getParameter(MonitorService.SUCCESS, 0);

+        int failure = url.getParameter(MonitorService.FAILURE, 0);

+        int input = url.getParameter(MonitorService.INPUT, 0);

+        int output = url.getParameter(MonitorService.OUTPUT, 0);

+        int elapsed = url.getParameter(MonitorService.ELAPSED, 0);

+        int concurrent = url.getParameter(MonitorService.CONCURRENT, 0);

+        // 初始化原子引用

+        Statistics statistics = new Statistics(url);

+        AtomicReference<long[]> reference = statisticsMap.get(statistics);

+        if (reference == null) {

+            statisticsMap.putIfAbsent(statistics, new AtomicReference<long[]>());

+            reference = statisticsMap.get(statistics);

+        }

+        // CompareAndSet并发加入统计数据

+        long[] current;

+        long[] update = new long[LENGTH];

+        do {

+            current = reference.get();

+            if (current == null) {

+                update[0] = success;

+                update[1] = failure;

+                update[2] = input;

+                update[3] = output;

+                update[4] = elapsed;

+                update[5] = concurrent;

+                update[6] = input;

+                update[7] = output;

+                update[8] = elapsed;

+                update[9] = concurrent;

+            } else {

+                update[0] = current[0] + success;

+                update[1] = current[1] + failure;

+                update[2] = current[2] + input;

+                update[3] = current[3] + output;

+                update[4] = current[4] + elapsed;

+                update[5] = (current[5] + concurrent) / 2;

+                update[6] = current[6] > input ? current[6] : input;

+                update[7] = current[7] > output ? current[7] : output;

+                update[8] = current[8] > elapsed ? current[8] : elapsed;

+                update[9] = current[9] > concurrent ? current[9] : concurrent;

+            }

+        } while (! reference.compareAndSet(current, update));

+    }

+

+    public URL getUrl() {

+        return monitorInvoker.getUrl();

+    }

+

+    public boolean isAvailable() {

+        return monitorInvoker.isAvailable();

+    }

+

+    public void destroy() {

+        try {

+            sendFuture.cancel(true);

+        } catch (Throwable t) {

+            logger.error("Unexpected error occur at cancel sender timer, cause: " + t.getMessage(), t);

+        }

+        monitorInvoker.destroy();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.java b/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.java
new file mode 100644
index 0000000..023894e
--- /dev/null
+++ b/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.dubbo;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.monitor.Monitor;

+import com.alibaba.dubbo.monitor.MonitorService;

+import com.alibaba.dubbo.monitor.support.AbstractMonitorFactroy;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+

+/**

+ * DefaultMonitorFactroy

+ * 

+ * @author william.liangf

+ */

+public class DubboMonitorFactroy extends AbstractMonitorFactroy {

+

+    private Protocol protocol;

+    

+    private ProxyFactory proxyFactory;

+

+    public void setProtocol(Protocol protocol) {

+        this.protocol = protocol;

+    }

+

+    public void setProxyFactory(ProxyFactory proxyFactory) {

+        this.proxyFactory = proxyFactory;

+    }

+    

+    @Override

+    protected Monitor createMonitor(URL url) {

+        url = url.setProtocol(url.getParameter(Constants.PROTOCOL_KEY, "dubbo"));

+        if (url.getPath() == null || url.getPath().length() == 0) {

+            url = url.setPath(MonitorService.class.getName());

+        }

+        String filter = url.getParameter(Constants.REFERENCE_FILTER_KEY);

+        if (filter == null || filter.length() == 0) {

+            filter = "";

+        } else {

+            filter = filter + ",";

+        }

+        url = url.addParameters(Constants.CLUSTER_KEY, "failsafe", Constants.CHECK_KEY, String.valueOf(false), 

+                Constants.REFERENCE_FILTER_KEY, filter + "-monitor");

+        Invoker<MonitorService> monitorInvoker = protocol.refer(MonitorService.class, url);

+        MonitorService monitorService = proxyFactory.getProxy(monitorInvoker);

+        return new DubboMonitor(monitorInvoker, monitorService);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.java b/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.java
new file mode 100644
index 0000000..45008e4
--- /dev/null
+++ b/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.java
@@ -0,0 +1,194 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.dubbo;

+

+import java.io.Serializable;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.monitor.MonitorService;

+

+/**

+ * Statistics. (SPI, Prototype, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class Statistics implements Serializable {

+    

+    private static final long serialVersionUID = -6921183057683641441L;

+    

+    private URL url;

+    

+    private String application;

+    

+    private String service;

+

+    private String method;

+    

+    private String group;

+

+    private String version;

+    

+    private String client;

+    

+    private String server;

+

+    public Statistics(URL url) {

+        this.url = url;

+        this.application = url.getParameter(MonitorService.APPLICATION);

+        this.service = url.getParameter(MonitorService.INTERFACE);

+        this.method = url.getParameter(MonitorService.METHOD);

+        this.group = url.getParameter(MonitorService.GROUP);

+        this.version = url.getParameter(MonitorService.VERSION);

+        this.client = url.getParameter(MonitorService.CONSUMER, url.getAddress());

+        this.server = url.getParameter(MonitorService.PROVIDER, url.getAddress());

+    }

+

+    public URL getUrl() {

+        return url;

+    }

+

+    public void setUrl(URL url) {

+        this.url = url;

+    }

+

+    public String getApplication() {

+        return application;

+    }

+

+    public Statistics setApplication(String application) {

+        this.application = application;

+        return this;

+    }

+

+    public String getService() {

+        return service;

+    }

+

+    public Statistics setService(String service) {

+        this.service = service;

+        return this;

+    }

+

+    public String getGroup() {

+        return group;

+    }

+

+    public void setGroup(String group) {

+        this.group = group;

+    }

+

+    public String getVersion() {

+        return version;

+    }

+

+    public void setVersion(String version) {

+        this.version = version;

+    }

+

+    public String getMethod() {

+        return method;

+    }

+

+    public Statistics setMethod(String method) {

+        this.method = method;

+        return this;

+    }

+

+    public String getClient() {

+        return client;

+    }

+

+    public Statistics setClient(String client) {

+        this.client = client;

+        return this;

+    }

+

+    public String getServer() {

+        return server;

+    }

+

+    public Statistics setServer(String server) {

+        this.server = server;

+        return this;

+    }

+

+    @Override

+    public int hashCode() {

+        final int prime = 31;

+        int result = 1;

+        result = prime * result + ((application == null) ? 0 : application.hashCode());

+        result = prime * result + ((client == null) ? 0 : client.hashCode());

+        result = prime * result + ((group == null) ? 0 : group.hashCode());

+        result = prime * result + ((method == null) ? 0 : method.hashCode());

+        result = prime * result + ((server == null) ? 0 : server.hashCode());

+        result = prime * result + ((service == null) ? 0 : service.hashCode());

+        result = prime * result + ((version == null) ? 0 : version.hashCode());

+        return result;

+    }

+

+    @Override

+    public boolean equals(Object obj) {

+        if (this == obj)

+            return true;

+        if (obj == null)

+            return false;

+        if (getClass() != obj.getClass())

+            return false;

+        Statistics other = (Statistics) obj;

+        if (application == null) {

+            if (other.application != null)

+                return false;

+        } else if (!application.equals(other.application))

+            return false;

+        if (client == null) {

+            if (other.client != null)

+                return false;

+        } else if (!client.equals(other.client))

+            return false;

+        if (group == null) {

+            if (other.group != null)

+                return false;

+        } else if (!group.equals(other.group))

+            return false;

+        if (method == null) {

+            if (other.method != null)

+                return false;

+        } else if (!method.equals(other.method))

+            return false;

+        if (server == null) {

+            if (other.server != null)

+                return false;

+        } else if (!server.equals(other.server))

+            return false;

+        if (service == null) {

+            if (other.service != null)

+                return false;

+        } else if (!service.equals(other.service))

+            return false;

+        if (version == null) {

+            if (other.version != null)

+                return false;

+        } else if (!version.equals(other.version))

+            return false;

+        return true;

+    }

+

+    @Override

+    public String toString() {

+        return url.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory b/dubbo-monitor-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory
new file mode 100644
index 0000000..25ae258
--- /dev/null
+++ b/dubbo-monitor-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory
@@ -0,0 +1 @@
+dubbo=com.alibaba.dubbo.monitor.dubbo.DubboMonitorFactroy
\ No newline at end of file
diff --git a/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java b/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java
new file mode 100644
index 0000000..8f81003
--- /dev/null
+++ b/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java
@@ -0,0 +1,143 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.dubbo;

+

+import junit.framework.Assert;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.monitor.Monitor;

+import com.alibaba.dubbo.monitor.MonitorFactory;

+import com.alibaba.dubbo.monitor.MonitorService;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * DubboMonitorTest

+ * 

+ * @author william.liangf

+ */

+public class DubboMonitorTest {

+    

+    private volatile URL lastStatistics;

+    

+    private final Invoker<MonitorService> monitorInvoker = new Invoker<MonitorService>() {

+        public Class<MonitorService> getInterface() {

+            return MonitorService.class;

+        }

+        public URL getUrl() {

+            return URL.valueOf("dubbo://127.0.0.1:7070?interval=20");

+        }

+        public boolean isAvailable() {

+            return false;

+        }

+        public Result invoke(Invocation invocation) throws RpcException {

+            return null;

+        }

+        public void destroy() {

+        }

+    };

+    

+    private final MonitorService monitorService = new MonitorService() {

+

+        public void count(URL statistics) {

+            DubboMonitorTest.this.lastStatistics = statistics;

+        }

+        

+    };

+    

+    @Test

+    public void testCount() throws Exception {

+        DubboMonitor monitor = new DubboMonitor(monitorInvoker, monitorService);

+        URL statistics = new URL("dubbo", "10.20.153.10", 0)

+            .addParameter(MonitorService.APPLICATION, "morgan")

+            .addParameter(MonitorService.INTERFACE, "MemberService")

+            .addParameter(MonitorService.METHOD, "findPerson")

+            .addParameter(MonitorService.CONSUMER, "10.20.153.11")

+            .addParameter(MonitorService.SUCCESS, 1)

+            .addParameter(MonitorService.FAILURE, 0)

+            .addParameter(MonitorService.ELAPSED, 3)

+            .addParameter(MonitorService.MAX_ELAPSED, 3)

+            .addParameter(MonitorService.CONCURRENT, 1)

+            .addParameter(MonitorService.MAX_CONCURRENT, 1);

+        monitor.count(statistics);

+        while (lastStatistics == null) {

+            Thread.sleep(10);

+        }

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.APPLICATION), "morgan");

+        Assert.assertEquals(lastStatistics.getProtocol(), "dubbo");

+        Assert.assertEquals(lastStatistics.getHost(), "10.20.153.10");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.APPLICATION), "morgan");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.INTERFACE), "MemberService");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.METHOD), "findPerson");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.CONSUMER), "10.20.153.11");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.SUCCESS), "1");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.FAILURE), "0");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.ELAPSED), "3");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.MAX_ELAPSED), "3");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.CONCURRENT), "1");

+        Assert.assertEquals(lastStatistics.getParameter(MonitorService.MAX_CONCURRENT), "1");

+        monitor.destroy();

+    }

+    

+    @Test

+    public void testMonitorFactory() throws Exception {

+        MockMonitorService monitorService = new MockMonitorService();

+        URL statistics = new URL("dubbo", "10.20.153.10", 0)

+                .addParameter(MonitorService.APPLICATION, "morgan")

+                .addParameter(MonitorService.INTERFACE, "MemberService")

+                .addParameter(MonitorService.METHOD, "findPerson")

+                .addParameter(MonitorService.CONSUMER, "10.20.153.11")

+                .addParameter(MonitorService.SUCCESS, 1)

+                .addParameter(MonitorService.FAILURE, 0)

+                .addParameter(MonitorService.ELAPSED, 3)

+                .addParameter(MonitorService.MAX_ELAPSED, 3)

+                .addParameter(MonitorService.CONCURRENT, 1)

+                .addParameter(MonitorService.MAX_CONCURRENT, 1);

+        

+        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+        ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+        MonitorFactory monitorFactory = ExtensionLoader.getExtensionLoader(MonitorFactory.class).getAdaptiveExtension();

+

+        Exporter<MonitorService> exporter = protocol.export(proxyFactory.getInvoker(monitorService, MonitorService.class, URL.valueOf("dubbo://127.0.0.1:17979/" + MonitorService.class.getName())));

+        try {

+            Monitor monitor = monitorFactory.getMonitor(URL.valueOf("dubbo://127.0.0.1:17979?interval=10"));

+            try {

+                monitor.count(statistics);

+                int i = 0;

+                while(monitorService.getStatistics() == null && i < 200) {

+                    i ++;

+                    Thread.sleep(10);

+                }

+                URL result = monitorService.getStatistics();

+                Assert.assertEquals(1, result.getParameter(MonitorService.SUCCESS, 0));

+                Assert.assertEquals(3, result.getParameter(MonitorService.ELAPSED, 0));

+            } finally {

+                monitor.destroy();

+            }

+        } finally {

+            exporter.unexport();

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/MockMonitorService.java b/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/MockMonitorService.java
new file mode 100644
index 0000000..b705bc2
--- /dev/null
+++ b/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/MockMonitorService.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.dubbo;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.monitor.MonitorService;

+

+/**

+ * MockMonitorService

+ * 

+ * @author william.liangf

+ */

+public class MockMonitorService implements MonitorService {

+    

+    private URL statistics;

+

+    public void count(URL statistics) {

+        this.statistics = statistics;

+    }

+

+    public URL getStatistics() {

+        return statistics;

+    }

+

+}

diff --git a/dubbo-monitor-simple/pom.xml b/dubbo-monitor-simple/pom.xml
new file mode 100644
index 0000000..82815df
--- /dev/null
+++ b/dubbo-monitor-simple/pom.xml
@@ -0,0 +1,92 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-monitor-simple</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Simple Monitor Module</name>
+	<description>The simple monitor module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>false</skip_maven_deploy>

+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>

+		<dependency>

+			<groupId>jfree</groupId>

+			<artifactId>jfreechart</artifactId>

+		</dependency>
+		<dependency>

+			<groupId>org.mortbay.jetty</groupId>

+			<artifactId>jetty</artifactId>

+		</dependency>

+		<dependency>

+		    <groupId>org.apache.zookeeper</groupId>

+		    <artifactId>zookeeper</artifactId>

+		</dependency>

+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<artifactId>maven-dependency-plugin</artifactId>

+				<executions>

+					<execution>

+						<id>unpack</id>

+						<phase>package</phase>

+						<goals>

+							<goal>unpack</goal>

+						</goals>

+						<configuration>

+							<artifactItems>

+								<artifactItem>

+									<groupId>com.alibaba</groupId>

+									<artifactId>dubbo</artifactId>

+									<version>${project.parent.version}</version>

+									<outputDirectory>${project.build.directory}/dubbo</outputDirectory>

+									<includes>META-INF/assembly/**</includes>

+								</artifactItem>

+							</artifactItems>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+			<plugin>

+				<artifactId>maven-assembly-plugin</artifactId>

+                <configuration>

+                    <descriptor>src/main/assembly/assembly.xml</descriptor>

+                </configuration>

+                <executions>

+					<execution>

+						<id>make-assembly</id>

+						<phase>package</phase>

+						<goals>

+							<goal>single</goal>

+						</goals>

+					</execution>

+				</executions>

+            </plugin>

+		</plugins>

+	</build>
+</project>
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/assembly/assembly.xml b/dubbo-monitor-simple/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..25235d9
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/assembly/assembly.xml
@@ -0,0 +1,39 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<assembly>

+	<id>assembly</id>

+	<formats>

+		<format>tar.gz</format>

+	</formats>

+	<includeBaseDirectory>true</includeBaseDirectory>

+	<fileSets>

+		<fileSet>

+			<directory>${project.build.directory}/dubbo/META-INF/assembly/bin</directory>

+			<outputDirectory>bin</outputDirectory>

+			<fileMode>0755</fileMode>

+		</fileSet>

+		<fileSet>

+			<directory>src/main/assembly/conf</directory>

+			<outputDirectory>conf</outputDirectory>

+			<fileMode>0644</fileMode>

+		</fileSet>

+	</fileSets>

+	<dependencySets>

+		<dependencySet>

+			<outputDirectory>lib</outputDirectory>

+		</dependencySet>

+	</dependencySets>

+</assembly>
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/assembly/conf/dubbo.properties b/dubbo-monitor-simple/src/main/assembly/conf/dubbo.properties
new file mode 100644
index 0000000..5aa8946
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/assembly/conf/dubbo.properties
@@ -0,0 +1,29 @@
+##

+# Copyright 1999-2011 Alibaba Group.

+#  

+# 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.

+##

+dubbo.container=log4j,spring,registry,jetty

+dubbo.application.name=simple-monitor

+dubbo.application.owner=

+dubbo.registry.address=multicast://224.5.6.7:1234

+#dubbo.registry.address=zookeeper://127.0.0.1:2181

+#dubbo.registry.address=redis://127.0.0.1:6379

+#dubbo.registry.address=dubbo://127.0.0.1:9090

+dubbo.protocol.port=7070

+dubbo.jetty.port=8080

+dubbo.jetty.directory=${user.home}/monitor

+dubbo.charts.directory=${dubbo.jetty.directory}/charts

+dubbo.statistics.directory=${user.home}/monitor/statistics

+dubbo.log4j.file=logs/dubbo-monitor-simple.log

+dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/CountUtils.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/CountUtils.java
new file mode 100644
index 0000000..4604e00
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/CountUtils.java
@@ -0,0 +1,93 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple;

+

+import java.io.BufferedReader;

+import java.io.File;

+import java.io.FileReader;

+import java.io.IOException;

+import java.util.regex.Pattern;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+

+/**

+ * CountUtils

+ * 

+ * @author william.liangf

+ */

+public class CountUtils {

+

+    private static final Logger logger = LoggerFactory.getLogger(CountUtils.class);

+

+    private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+");

+    

+    private static final int SUM = 0;

+    

+    private static final int MAX = 1;

+    

+    private static final int AVG = 2;

+    

+    public static long sum(File file) {

+        return calc(file, SUM);

+    }

+

+    public static long max(File file) {

+        return calc(file, SUM);

+    }

+

+    public static long avg(File file) {

+        return calc(file, SUM);

+    }

+    

+    private static long calc(File file, int op) {

+        if (file.exists()) {

+            try {

+                BufferedReader reader = new BufferedReader(new FileReader(file));

+                try {

+                    int times = 0;

+                    int count = 0;

+                    String line;

+                    while ((line = reader.readLine()) != null) {

+                        int i = line.indexOf(" ");

+                        if (i > 0) {

+                            line = line.substring(i + 1).trim();

+                            if (NUMBER_PATTERN.matcher(line).matches()) {

+                                int value = Integer.parseInt(line);

+                                times ++;

+                                if (op == MAX) {

+                                    count = Math.max(count, value);

+                                } else {

+                                    count += value;

+                                }

+                            }

+                        }

+                    }

+                    if (op == AVG) {

+                        return count / times;

+                    }

+                    return count;

+                } finally {

+                    reader.close();

+                }

+            } catch (IOException e) {

+                logger.warn(e.getMessage(), e);

+            }

+        }

+        return 0;

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/RegistryContainer.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/RegistryContainer.java
new file mode 100644
index 0000000..ca49a3b
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/RegistryContainer.java
@@ -0,0 +1,340 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple;

+

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.container.Container;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.RegistryFactory;

+

+/**

+ * RegistryContainer

+ * 

+ * @author william.liangf

+ */

+public class RegistryContainer implements Container {

+

+    private static final Logger logger = LoggerFactory.getLogger(RegistryContainer.class);

+

+    public static final String REGISTRY_ADDRESS = "dubbo.registry.address";

+

+    private final RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();

+

+    private final Set<String> applications = new ConcurrentHashSet<String>();

+

+    private final Map<String, Set<String>> providerServiceApplications = new ConcurrentHashMap<String, Set<String>>();

+

+    private final Map<String, Set<String>> providerApplicationServices = new ConcurrentHashMap<String, Set<String>>();

+

+    private final Map<String, Set<String>> consumerServiceApplications = new ConcurrentHashMap<String, Set<String>>();

+

+    private final Map<String, Set<String>> consumerApplicationServices = new ConcurrentHashMap<String, Set<String>>();

+

+    private final Set<String> services = new ConcurrentHashSet<String>();

+

+    private final Map<String, List<URL>> serviceProviders = new ConcurrentHashMap<String, List<URL>>();

+

+    private final Map<String, List<URL>> serviceConsumers = new ConcurrentHashMap<String, List<URL>>();

+

+    private final Map<String, List<URL>> serviceRoutes = new ConcurrentHashMap<String, List<URL>>();

+

+    private Registry registry;

+    

+    private static RegistryContainer INSTANCE = null;

+    

+    public RegistryContainer() {

+        INSTANCE = this;

+    }

+    

+    public static RegistryContainer getInstance() {

+        if (INSTANCE == null) {

+            ExtensionLoader.getExtensionLoader(Container.class).getExtension("registry");

+        }

+        return INSTANCE;

+    }

+

+    public Registry getRegistry() {

+        return registry;

+    }

+

+    public Set<String> getApplications() {

+        return Collections.unmodifiableSet(applications);

+    }

+    

+    public Set<String> getDependencies(String application, boolean reverse) {

+        if (reverse) {

+            Set<String> dependencies = new HashSet<String>();

+            Set<String> services = providerApplicationServices.get(application);

+            if (services != null && services.size() > 0) {

+                for (String service : services) {

+                    Set<String> applications = consumerServiceApplications.get(service);

+                    if (applications != null && applications.size() > 0) {

+                        dependencies.addAll(applications);

+                    }

+                }

+            }

+            return dependencies;

+        } else {

+            Set<String> dependencies = new HashSet<String>();

+            Set<String> services = consumerApplicationServices.get(application);

+            if (services != null && services.size() > 0) {

+                for (String service : services) {

+                    Set<String> applications = providerServiceApplications.get(service);

+                    if (applications != null && applications.size() > 0) {

+                        dependencies.addAll(applications);

+                    }

+                }

+            }

+            return dependencies;

+        }

+    }

+

+    public Set<String> getServices() {

+        return Collections.unmodifiableSet(services);

+    }

+

+    public Map<String, List<URL>> getServiceProviders() {

+        return Collections.unmodifiableMap(serviceProviders);

+    }

+

+    public List<URL> getProvidersByService(String service) {

+        List<URL> urls = serviceProviders.get(service);

+        return urls == null ? null : Collections.unmodifiableList(urls);

+    }

+

+    public List<URL> getProvidersByHost(String host) {

+        List<URL> urls = new ArrayList<URL>();

+        if (host != null && host.length() > 0) {

+            for (List<URL> providers : serviceProviders.values()) {

+                for (URL url : providers) {

+                    if (host.equals(url.getHost())) {

+                        urls.add(url);

+                    }

+                }

+            }

+        }

+        return urls;

+    }

+

+    public List<URL> getProvidersByApplication(String application) {

+        List<URL> urls = new ArrayList<URL>();

+        if (application != null && application.length() > 0) {

+            for (List<URL> providers : serviceProviders.values()) {

+                for (URL url : providers) {

+                    if (application.equals(url.getParameter(Constants.APPLICATION_KEY))) {

+                        urls.add(url);

+                    }

+                }

+            }

+        }

+        return urls;

+    }

+

+    public Set<String> getHosts() {

+        Set<String> addresses = new HashSet<String>();

+        for (List<URL> providers : serviceProviders.values()) {

+            for (URL url : providers) {

+                addresses.add(url.getHost());

+            }

+        }

+        for (List<URL> providers : serviceConsumers.values()) {

+            for (URL url : providers) {

+                addresses.add(url.getHost());

+            }

+        }

+        return addresses;

+    }

+

+    public Map<String, List<URL>> getServiceConsumers() {

+        return Collections.unmodifiableMap(serviceConsumers);

+    }

+

+    public List<URL> getConsumersByService(String service) {

+        List<URL> urls = serviceConsumers.get(service);

+        return urls == null ? null : Collections.unmodifiableList(urls);

+    }

+

+    public List<URL> getConsumersByHost(String host) {

+        List<URL> urls = new ArrayList<URL>();

+        if (host != null && host.length() > 0) {

+            for (List<URL> consumers : serviceConsumers.values()) {

+                for (URL url : consumers) {

+                    if (host.equals(url.getHost())) {

+                        urls.add(url);

+                    }

+                }

+            }

+        }

+        return Collections.unmodifiableList(urls);

+    }

+

+    public List<URL> getConsumersByApplication(String application) {

+        List<URL> urls = new ArrayList<URL>();

+        if (application != null && application.length() > 0) {

+            for (List<URL> consumers : serviceConsumers.values()) {

+                for (URL url : consumers) {

+                    if (application.equals(url.getParameter(Constants.APPLICATION_KEY))) {

+                        urls.add(url);

+                    }

+                }

+            }

+        }

+        return urls;

+    }

+

+    public Map<String, List<URL>> getServiceRoutes() {

+        return Collections.unmodifiableMap(serviceRoutes);

+    }

+

+    public List<URL> getRoutesByService(String service) {

+        List<URL> urls = serviceRoutes.get(service);

+        return urls == null ? null : Collections.unmodifiableList(urls);

+    }

+

+    public void start() {

+        String url = ConfigUtils.getProperty(REGISTRY_ADDRESS);

+        if (url == null || url.length() == 0) {

+            throw new IllegalArgumentException("Please set java start argument: -D" + REGISTRY_ADDRESS + "=zookeeper://127.0.0.1:2181");

+        }

+        URL registryUrl = URL.valueOf(url);

+        registry = registryFactory.getRegistry(registryUrl);

+        URL subscribeUrl = new URL(Constants.SUBSCRIBE_PROTOCOL, NetUtils.getLocalHost(), 0)

+                        .addParameters(Constants.ADMIN_KEY, String.valueOf(true),

+                                Constants.INTERFACE_KEY, Constants.ANY_VALUE, 

+                                Constants.GROUP_KEY, Constants.ANY_VALUE, 

+                                Constants.VERSION_KEY, Constants.ANY_VALUE);

+        registry.subscribe(subscribeUrl, new NotifyListener() {

+            public void notify(List<URL> urls) {

+                if (urls == null || urls.size() == 0) {

+                    return;

+                }

+                Set<String> notifiedServices = new HashSet<String>();

+                Map<String, List<URL>> proivderMap = new HashMap<String, List<URL>>();

+                Map<String, List<URL>> consumerMap = new HashMap<String, List<URL>>();

+                Map<String, List<URL>> routeMap = new HashMap<String, List<URL>>();

+                for (URL url : urls) {

+                    String application = url.getParameter(Constants.APPLICATION_KEY);

+                    if (application != null && application.length() > 0) {

+                        applications.add(application);

+                    }

+                    String service = url.getServiceInterface();

+                    notifiedServices.add(service);

+                    services.add(service);

+                    if (Constants.ROUTE_PROTOCOL.equals(url.getProtocol())) {

+                        List<URL> list = routeMap.get(service);

+                        if (list == null) {

+                            list = new ArrayList<URL>();

+                            routeMap.put(service, list);

+                        }

+                        list.add(url);

+                    } else if (Constants.SUBSCRIBE_PROTOCOL.equals(url.getProtocol())) {

+                        List<URL> list = consumerMap.get(service);

+                        if (list == null) {

+                            list = new ArrayList<URL>();

+                            consumerMap.put(service, list);

+                        }

+                        list.add(url);

+

+                        if (application != null && application.length() > 0) {

+                            Set<String> serviceApplications = consumerServiceApplications.get(service);

+                            if (serviceApplications == null) {

+                                consumerServiceApplications.put(service, new ConcurrentHashSet<String>());

+                                serviceApplications = consumerServiceApplications.get(service);

+                            }

+                            serviceApplications.add(application);

+    

+                            Set<String> applicationServices = consumerApplicationServices.get(application);

+                            if (applicationServices == null) {

+                                consumerApplicationServices.put(application, new ConcurrentHashSet<String>());

+                                applicationServices = consumerApplicationServices.get(application);

+                            }

+                            applicationServices.add(service);

+                        }

+                    } else if (! Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {

+                        List<URL> list = proivderMap.get(service);

+                        if (list == null) {

+                            list = new ArrayList<URL>();

+                            proivderMap.put(service, list);

+                        }

+                        list.add(url);

+

+                        if (application != null && application.length() > 0) {

+                            Set<String> serviceApplications = providerServiceApplications.get(service);

+                            if (serviceApplications == null) {

+                                providerServiceApplications.put(service, new ConcurrentHashSet<String>());

+                                serviceApplications = providerServiceApplications.get(service);

+                            }

+                            serviceApplications.add(application);

+    

+                            Set<String> applicationServices = providerApplicationServices.get(application);

+                            if (applicationServices == null) {

+                                providerApplicationServices.put(application, new ConcurrentHashSet<String>());

+                                applicationServices = providerApplicationServices.get(application);

+                            }

+                            applicationServices.add(service);

+                        }

+                    }

+                }

+                if (proivderMap != null && proivderMap.size() > 0) {

+                    serviceProviders.putAll(proivderMap);

+                }

+                if (consumerMap != null && consumerMap.size() > 0) {

+                    serviceConsumers.putAll(consumerMap);

+                }

+                if (routeMap != null && routeMap.size() > 0) {

+                    serviceRoutes.putAll(routeMap);

+                }

+                for (String service : notifiedServices) {

+                    if (! proivderMap.containsKey(service)) {

+                        serviceProviders.remove(service);

+                    }

+                    if (! consumerMap.containsKey(service)) {

+                        serviceConsumers.remove(service);

+                    }

+                    if (! routeMap.containsKey(service)) {

+                        serviceRoutes.remove(service);

+                    }

+                }

+            }

+        });

+    }

+

+    public void stop() {

+        try {

+            registry.destroy();

+        } catch (Throwable e) {

+            logger.error(e.getMessage(), e);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorService.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorService.java
new file mode 100644
index 0000000..e6e09dd
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorService.java
@@ -0,0 +1,425 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple;

+

+import java.awt.Color;

+import java.awt.image.BufferedImage;

+import java.io.BufferedReader;

+import java.io.File;

+import java.io.FileOutputStream;

+import java.io.FileReader;

+import java.io.FileWriter;

+import java.io.IOException;

+import java.text.DecimalFormat;

+import java.text.ParseException;

+import java.text.SimpleDateFormat;

+import java.util.Date;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.concurrent.BlockingQueue;

+import java.util.concurrent.Executors;

+import java.util.concurrent.LinkedBlockingQueue;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+

+import javax.imageio.ImageIO;

+

+import org.jfree.chart.ChartFactory;

+import org.jfree.chart.JFreeChart;

+import org.jfree.chart.axis.DateAxis;

+import org.jfree.chart.plot.XYPlot;

+import org.jfree.data.time.Minute;

+import org.jfree.data.time.TimeSeries;

+import org.jfree.data.time.TimeSeriesCollection;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.monitor.MonitorService;

+

+/**

+ * SimpleMonitorService

+ * 

+ * @author william.liangf

+ */

+public class SimpleMonitorService implements MonitorService {

+

+    private static final Logger logger = LoggerFactory.getLogger(SimpleMonitorService.class);

+

+    private static final String[] types = {SUCCESS, FAILURE, ELAPSED, CONCURRENT, MAX_ELAPSED, MAX_CONCURRENT};

+    

+    private static final String POISON_PROTOCOL = "poison";

+    

+    // 定时任务执行器

+    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboMonitorTimer", true));

+

+    // 图表绘制定时器

+    private final ScheduledFuture<?> chartFuture;

+

+    private final Thread writeThread;

+    

+    private final BlockingQueue<URL> queue;

+    

+    private String statisticsDirectory = "statistics";

+

+    private String chartsDirectory = "charts";

+    

+    private volatile boolean running = true;

+    

+    private static SimpleMonitorService INSTANCE = null;

+

+    public static SimpleMonitorService getInstance() {

+        return INSTANCE;

+    }

+

+    public String getStatisticsDirectory() {

+        return statisticsDirectory;

+    }

+    

+    public void setStatisticsDirectory(String statistics) {

+        if (statistics != null) {

+            this.statisticsDirectory = statistics;

+        }

+    }

+

+    public String getChartsDirectory() {

+        return chartsDirectory;

+    }

+

+    public void setChartsDirectory(String charts) {

+        if (charts != null) {

+            this.chartsDirectory = charts;

+        }

+    }

+    

+    public SimpleMonitorService() {

+        queue = new LinkedBlockingQueue<URL>(Integer.parseInt(ConfigUtils.getProperty("dubbo.monitor.queue", "100000")));

+        writeThread = new Thread(new Runnable() {

+            public void run() {

+                while (running) {

+                    try {

+                        write(); // 记录统计日志

+                    } catch (Throwable t) { // 防御性容错

+                        logger.error("Unexpected error occur at write stat log, cause: " + t.getMessage(), t);

+                        try {

+                            Thread.sleep(5000); // 失败延迟

+                        } catch (Throwable t2) {

+                        }

+                    }

+                }

+            }

+        });

+        writeThread.setDaemon(true);

+        writeThread.setName("DubboMonitorAsyncWriteLogThread");

+        writeThread.start();

+        chartFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {

+            public void run() {

+                try {

+                    draw(); // 绘制图表

+                } catch (Throwable t) { // 防御性容错

+                    logger.error("Unexpected error occur at draw stat chart, cause: " + t.getMessage(), t);

+                }

+            }

+        }, 1, 300, TimeUnit.SECONDS);

+        INSTANCE = this;

+    }

+

+    public void close() {

+        try {

+            running = false;

+            queue.offer(new URL(POISON_PROTOCOL, NetUtils.LOCALHOST, 0));

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+        try {

+            chartFuture.cancel(true);

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+    }

+    

+    private void write() throws Exception {

+        URL statistics = queue.take();

+        if (POISON_PROTOCOL.equals(statistics.getProtocol())) {

+            return;

+        }

+        String timestamp = statistics.getParameter(Constants.TIMESTAMP_KEY);

+        Date now;

+        if (timestamp == null || timestamp.length() == 0) {

+            now = new Date();

+        } else if (timestamp.length() == "yyyyMMddHHmmss".length()) {

+            now = new SimpleDateFormat("yyyyMMddHHmmss").parse(timestamp);

+        } else {

+            now = new Date(Long.parseLong(timestamp));

+        }

+        String day = new SimpleDateFormat("yyyyMMdd").format(now);

+        SimpleDateFormat format = new SimpleDateFormat("HHmm");

+        for (String key : types) {

+            try {

+                String type;

+                String consumer;

+                String provider;

+                if (statistics.hasParameter(PROVIDER)) {

+                    type = PROVIDER;

+                    consumer = statistics.getHost();

+                    provider = statistics.getParameter(PROVIDER);

+                    int i = provider.indexOf(':');

+                    if (i > 0) {

+                        provider = provider.substring(0, i);

+                    }

+                } else {

+                    type = CONSUMER;

+                    consumer = statistics.getParameter(CONSUMER);

+                    int i = consumer.indexOf(':');

+                    if (i > 0) {

+                        consumer = consumer.substring(0, i);

+                    }

+                    provider = statistics.getHost();

+                }

+                String filename = statisticsDirectory 

+                        + "/" + day 

+                        + "/" + statistics.getServiceInterface() 

+                        + "/" + statistics.getParameter(METHOD) 

+                        + "/" + consumer 

+                        + "/" + provider 

+                        + "/" + type + "." + key;

+                File file = new File(filename);

+                File dir = file.getParentFile();

+                if (dir != null && ! dir.exists()) {

+                    dir.mkdirs();

+                }

+                FileWriter writer = new FileWriter(file, true);

+                try {

+                    writer.write(format.format(now) + " " + statistics.getParameter(key, 0) + "\n");

+                    writer.flush();

+                } finally {

+                    writer.close();

+                }

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+    }

+

+    private void draw() {

+        File rootDir = new File(statisticsDirectory);

+        if (! rootDir.exists()) {

+            return;

+        }

+        File[] dateDirs = rootDir.listFiles();

+        for (File dateDir : dateDirs) {

+            File[] serviceDirs = dateDir.listFiles();

+            for (File serviceDir : serviceDirs) {

+                File[] methodDirs = serviceDir.listFiles();

+                for (File methodDir : methodDirs) {

+                    String methodUri = chartsDirectory + "/" + dateDir.getName() + "/" + serviceDir.getName() + "/" + methodDir.getName();

+                    

+                    File successFile = new File(methodUri + "/" + SUCCESS + ".png");

+                    long successModified = successFile.lastModified();

+                    boolean successChanged = false;

+                    Map<String, long[]> successData = new HashMap<String, long[]>();

+                    double[] successSummary = new double[4];

+                    

+                    File elapsedFile = new File(methodUri + "/" + ELAPSED + ".png");

+                    long elapsedModified = elapsedFile.lastModified();

+                    boolean elapsedChanged = false;

+                    Map<String, long[]> elapsedData = new HashMap<String, long[]>();

+                    double[] elapsedSummary = new double[4];

+                    long elapsedMax = 0;

+                    

+                    File[] consumerDirs = methodDir.listFiles();

+                    for (File consumerDir : consumerDirs) {

+                        File[] providerDirs = consumerDir.listFiles();

+                        for (File providerDir : providerDirs) {

+                            File consumerSuccessFile = new File(providerDir, CONSUMER + "." + SUCCESS);

+                            File providerSuccessFile = new File(providerDir, PROVIDER + "." + SUCCESS);

+                            appendData(new File[] {consumerSuccessFile, providerSuccessFile}, successData, successSummary);

+                            if (consumerSuccessFile.lastModified() > successModified 

+                                    || providerSuccessFile.lastModified() > successModified) {

+                                successChanged = true;

+                            }

+                            

+                            File consumerElapsedFile = new File(providerDir, CONSUMER + "." + ELAPSED);

+                            File providerElapsedFile = new File(providerDir, PROVIDER + "." + ELAPSED);

+                            appendData(new File[] {consumerElapsedFile, providerElapsedFile}, elapsedData, elapsedSummary);

+                            elapsedMax = Math.max(elapsedMax, CountUtils.max(new File(providerDir, CONSUMER + "." + MAX_ELAPSED)));

+                            elapsedMax = Math.max(elapsedMax, CountUtils.max(new File(providerDir, PROVIDER + "." + MAX_ELAPSED)));

+                            if (consumerElapsedFile.lastModified() > elapsedModified 

+                                    || providerElapsedFile.lastModified() > elapsedModified) {

+                                elapsedChanged = true;

+                            }

+                        }

+                    }

+                    if (elapsedChanged) {

+                        divData(elapsedData, successData);

+                        elapsedSummary[0] = elapsedMax;

+                        elapsedSummary[1] = -1;

+                        elapsedSummary[2] = successSummary[3] == 0 ? 0 : elapsedSummary[3] / successSummary[3];

+                        elapsedSummary[3] = -1;

+                        createChart("ms/t", serviceDir.getName(), methodDir.getName(), dateDir.getName(), new String[] {CONSUMER, PROVIDER}, elapsedData, elapsedSummary, elapsedFile.getAbsolutePath());

+                    }

+                    if (successChanged) {

+                        divData(successData, 60);

+                        successSummary[0] = successSummary[0] / 60;

+                        successSummary[1] = successSummary[1] / 60;

+                        successSummary[2] = successSummary[2] / 60;

+                        createChart("t/s", serviceDir.getName(), methodDir.getName(), dateDir.getName(), new String[] {CONSUMER, PROVIDER}, successData, successSummary, successFile.getAbsolutePath());

+                    }

+                }

+            }

+        }

+    }

+    

+    private void divData(Map<String, long[]> successMap, long unit) {

+        for (long[] success : successMap.values()) {

+            for (int i = 0; i < success.length; i ++) {

+                success[i] = success[i] / unit;

+            }

+        }

+    }

+    

+    private void divData(Map<String, long[]> elapsedMap, Map<String, long[]> successMap) {

+        for (Map.Entry<String, long[]> entry : elapsedMap.entrySet()) {

+            long[] elapsed = entry.getValue();

+            long[] success = successMap.get(entry.getKey());

+            for (int i = 0; i < elapsed.length; i ++) {

+                elapsed[i] = success[i] == 0 ? 0 : elapsed[i] / success[i];

+            }

+        }

+    }

+

+    private void appendData(File[] files, Map<String, long[]> data, double[] summary) {

+        for (int i = 0; i < files.length; i ++) {

+            File file = files[i];

+            if (! file.exists()) {

+                continue;

+            }

+            try {

+                BufferedReader reader = new BufferedReader(new FileReader(file));

+                try {

+                    int sum = 0;

+                    int cnt = 0;

+                    String line;

+                    while ((line = reader.readLine()) != null) {

+                        int index = line.indexOf(" ");

+                        if (index > 0) {

+                            String key = line.substring(0, index).trim();

+                            long value = Long.parseLong(line.substring(index + 1).trim());

+                            long[] values = data.get(key);

+                            if (values == null) {

+                                values = new long[files.length];

+                                data.put(key, values);

+                            }

+                            values[i] += value;

+                            summary[0] = Math.max(summary[0], values[i]);

+                            summary[1] = summary[1] == 0 ? values[i] : Math.min(summary[1], values[i]);

+                            sum += value;

+                            cnt ++;

+                        }

+                    }

+                    if (i == 0) {

+                        summary[3] += sum;

+                        summary[2] = (summary[2] + (sum / cnt)) / 2;

+                    }

+                } finally {

+                    reader.close();

+                }

+            } catch (IOException e) {

+                logger.warn(e.getMessage(), e);

+            }

+        }

+    }

+    

+    private static void createChart(String key, String service, String method, String date, String[] types, Map<String, long[]> data, double[] summary, String path) {

+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm");

+        DecimalFormat numberFormat = new DecimalFormat("###,##0.##");

+        TimeSeriesCollection xydataset = new TimeSeriesCollection();

+        for (int i = 0; i < types.length; i ++) {

+            String type = types[i];

+            TimeSeries timeseries = new TimeSeries(type);

+            for (Map.Entry<String, long[]> entry : data.entrySet()) {

+                try {

+                    timeseries.add(new Minute(dateFormat.parse(date + entry.getKey())), entry.getValue()[i]);

+                } catch (ParseException e) {

+                    logger.error(e.getMessage(), e);

+                }

+            }

+            xydataset.addSeries(timeseries);

+        }

+        JFreeChart jfreechart = ChartFactory.createTimeSeriesChart(

+                "max: " + numberFormat.format(summary[0]) + (summary[1] >=0 ? " min: " + numberFormat.format(summary[1]) : "") 

+                + " avg: " + numberFormat.format(summary[2]) + (summary[3] >=0 ? " sum: " + numberFormat.format(summary[3]) : ""), 

+                toDisplayService(service) + "  " + method + "  " + toDisplayDate(date), key, xydataset, true, true, false);

+        jfreechart.setBackgroundPaint(Color.WHITE);

+        XYPlot xyplot = (XYPlot) jfreechart.getPlot();

+        xyplot.setBackgroundPaint(Color.WHITE);

+        xyplot.setDomainGridlinePaint(Color.GRAY);

+        xyplot.setRangeGridlinePaint(Color.GRAY);

+        xyplot.setDomainGridlinesVisible(true);

+        xyplot.setRangeGridlinesVisible(true);

+        DateAxis dateaxis = (DateAxis) xyplot.getDomainAxis();

+        dateaxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));

+        BufferedImage image = jfreechart.createBufferedImage(600, 300);

+        try {

+            if (logger.isInfoEnabled()) {

+                logger.info("write chart: " + path);

+            }

+            File methodChartFile = new File(path);

+            File methodChartDir = methodChartFile.getParentFile();

+            if (methodChartDir != null && ! methodChartDir.exists()) {

+                methodChartDir.mkdirs();

+            }

+            FileOutputStream output = new FileOutputStream(methodChartFile);

+            try {

+                ImageIO.write(image, "png", output);

+                output.flush();

+            } finally {

+                output.close();

+            }

+        } catch (IOException e) {

+            logger.warn(e.getMessage(), e);

+        }

+    }

+    

+    private static String toDisplayService(String service) {

+        int i = service.lastIndexOf('.');

+        if (i >= 0) {

+            return service.substring(i + 1);

+        }

+        return service;

+    }

+    

+    private static String toDisplayDate(String date) {

+        try {

+            return new SimpleDateFormat("yyyy-MM-dd").format(new SimpleDateFormat("yyyyMMdd").parse(date));

+        } catch (ParseException e) {

+            return date;

+        }

+    }

+

+    public void count(URL statistics) {

+        queue.offer(statistics);

+        if (logger.isInfoEnabled()) {

+            logger.info("collect statistics: " + statistics);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ApplicationsPageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ApplicationsPageHandler.java
new file mode 100644
index 0000000..69e0687
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ApplicationsPageHandler.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Menu;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.simple.RegistryContainer;

+

+/**

+ * ApplicationsPageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "Applications", desc = "Show application dependencies.", order = 1000)

+public class ApplicationsPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        Set<String> applications = RegistryContainer.getInstance().getApplications();

+        List<List<String>> rows = new ArrayList<List<String>>();

+        int providersCount = 0;

+        int consumersCount = 0;

+        int efferentCount = 0;

+        int afferentCount = 0;

+        if (applications != null && applications.size() > 0) {

+            for (String application : applications) {

+                List<String> row = new ArrayList<String>();

+                row.add(application);

+                

+                List<URL> providers = RegistryContainer.getInstance().getProvidersByApplication(application);

+                List<URL> consumers = RegistryContainer.getInstance().getConsumersByApplication(application);

+

+                if (providers != null && providers.size() > 0

+                        || consumers != null && consumers.size() > 0) {

+                    URL provider = (providers != null && providers.size() > 0 ? providers.iterator().next() : consumers.iterator().next());

+                    row.add(provider.getParameter("owner", "") + (provider.hasParameter("organization") ?  " (" + provider.getParameter("organization") + ")" : ""));

+                } else {

+                    row.add("");

+                }

+                

+                int providersSize = providers == null ? 0 : providers.size();

+                providersCount += providersSize;

+                row.add(providersSize == 0 ? "<font color=\"blue\">No provider</font>" : "<a href=\"providers.html?application=" + application + "\">Providers(" + providersSize + ")</a>");

+                

+                int consumersSize = consumers == null ? 0 : consumers.size();

+                consumersCount += consumersSize;

+                row.add(consumersSize == 0 ? "<font color=\"blue\">No consumer</font>" : "<a href=\"consumers.html?application=" + application + "\">Consumers(" + consumersSize + ")</a>");

+                

+                Set<String> efferents = RegistryContainer.getInstance().getDependencies(application, false);

+                int efferentSize = efferents == null ? 0 : efferents.size();

+                efferentCount += efferentSize;

+                row.add(efferentSize == 0 ? "<font color=\"blue\">No dependency</font>" : "<a href=\"dependencies.html?application=" + application + "\">Depends On(" + efferentSize + ")</a>");

+                

+                Set<String> afferents = RegistryContainer.getInstance().getDependencies(application, true);

+                int afferentSize = afferents == null ? 0 : afferents.size();

+                afferentCount += afferentSize;

+                row.add(afferentSize == 0 ? "<font color=\"blue\">No used</font>" : "<a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By(" + afferentSize + ")</a>");

+                rows.add(row);

+            }

+        }

+        return new Page("Applications", "Applications (" + rows.size() + ")",

+                new String[] { "Application Name:", "Owner", "Providers(" + providersCount + ")", "Consumers(" + consumersCount + ")", "Depends On(" + efferentCount + ")", "Used By(" + afferentCount + ")" }, rows);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ChartsPageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ChartsPageHandler.java
new file mode 100644
index 0000000..5201d44
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ChartsPageHandler.java
@@ -0,0 +1,96 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import java.io.File;

+import java.text.SimpleDateFormat;

+import java.util.ArrayList;

+import java.util.Date;

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.MonitorService;

+import com.alibaba.dubbo.monitor.simple.SimpleMonitorService;

+

+/**

+ * ChartsPageHandler

+ * 

+ * @author william.liangf

+ */

+public class ChartsPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        String service = url.getParameter("service");

+        if (service == null || service.length() == 0) {

+            throw new IllegalArgumentException("Please input service parameter.");

+        }

+        String date = url.getParameter("date");

+        if (date == null || date.length() == 0) {

+            date = new SimpleDateFormat("yyyyMMdd").format(new Date());

+        }

+        List<List<String>> rows = new ArrayList<List<String>>();

+        String directory = SimpleMonitorService.getInstance().getChartsDirectory();

+        File chartsDir = new File(directory);

+        String filename = directory + "/" + date + "/" + service;

+        File serviceDir = new File(filename);

+        if (serviceDir.exists()) {

+            File[] methodDirs = serviceDir.listFiles();

+            for (File methodDir : methodDirs) {

+                String methodUri = chartsDir.getName() + "/" + date + "/" + service + "/" + methodDir.getName() + "/";

+                rows.add(toRow(methodDir, methodUri));

+            }

+        }

+        StringBuilder nav = new StringBuilder();

+        nav.append("<a href=\"services.html\">Services</a> &gt; ");

+        nav.append(service);

+        nav.append(" &gt; <a href=\"providers.html?service=");

+        nav.append(service);

+        nav.append("\">Providers</a> | <a href=\"consumers.html?service=");

+        nav.append(service);

+        nav.append("\">Consumers</a> | <a href=\"statistics.html?service=");

+        nav.append(service);

+        nav.append("&date=");

+        nav.append(date);

+        nav.append("\">Statistics</a> | Charts &gt; <input type=\"text\" style=\"width: 65px;\" name=\"date\" value=\"");

+        nav.append(date);

+        nav.append("\" onkeyup=\"if (event.keyCode == 10 || event.keyCode == 13) {window.location.href='charts.html?service=");

+        nav.append(service);

+        nav.append("&date=' + this.value;}\" />");

+        return new Page(nav.toString(), "Charts (" + rows.size() + ")",

+                new String[] { "Method", "Requests per second (QPS)", "Average response time (ms)"}, rows);

+    }

+    

+    private List<String> toRow(File dir, String uri) {

+        List<String> row = new ArrayList<String>();

+        row.add(dir.getName());

+        if (new File(dir, MonitorService.SUCCESS + ".png").exists()) {

+            String url = uri + MonitorService.SUCCESS + ".png";

+            row.add("<a href=\"" + url + "\" target=\"_blank\"><img src=\"" + url + "\" style=\"width: 100%;\" border=\"0\" /></a>");

+        } else {

+            row.add("");

+        }

+        if (new File(dir, MonitorService.ELAPSED + ".png").exists()) {

+            String url = uri + MonitorService.ELAPSED + ".png";

+            row.add("<a href=\"" + url + "\" target=\"_blank\"><img src=\"" + url + "\" style=\"width: 100%;\" border=\"0\" /></a>");

+        } else {

+            row.add("");

+        }

+        return row;

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ConsumersPageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ConsumersPageHandler.java
new file mode 100644
index 0000000..74d7dc2
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ConsumersPageHandler.java
@@ -0,0 +1,89 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.simple.RegistryContainer;

+

+/**

+ * ConsumersPageHandler

+ * 

+ * @author william.liangf

+ */

+public class ConsumersPageHandler implements PageHandler {

+    

+    public Page handle(URL url) {

+        String service = url.getParameter("service");

+        String host = url.getParameter("host");

+        String application = url.getParameter("application");

+        if (service != null && service.length() > 0) {

+            List<List<String>> rows = new ArrayList<List<String>>();

+            List<URL> consumers = RegistryContainer.getInstance().getConsumersByService(service);

+            if (consumers != null && consumers.size() > 0) {

+                for (URL u : consumers) {

+                    List<String> row = new ArrayList<String>();

+                    String s = u.toFullString();

+                    row.add(s.replace("&", "&amp;"));

+                    row.add("<button onclick=\"if(confirm('Confirm unsubscribe consumer?')){window.location.href='unsubscribe.html?service=" + service + "&consumer=" + URL.encode(s) + "';}\">Unsubscribe</button>");

+                    rows.add(row);

+                }

+            }

+            return new Page("<a href=\"services.html\">Services</a> &gt; " + service 

+                    + " &gt; <a href=\"providers.html?service=" + service 

+                    + "\">Providers</a> | Consumers | <a href=\"statistics.html?service=" + service 

+                    + "\">Statistics</a> | <a href=\"charts.html?service=" + service 

+                    + "\">Charts</a>", "Consumers (" + rows.size() + ")",

+                    new String[] { "Consumer URL:", "Unsubscribe" }, rows);

+        } else if (host != null && host.length() > 0) {

+            List<List<String>> rows = new ArrayList<List<String>>();

+            List<URL> consumers = RegistryContainer.getInstance().getConsumersByHost(host);

+            if (consumers != null && consumers.size() > 0) {

+                for (URL u : consumers) {

+                    List<String> row = new ArrayList<String>();

+                    String s = u.toFullString();

+                    row.add(s.replace("&", "&amp;"));

+                    row.add("<button onclick=\"if(confirm('Confirm unsubscribe consumer?')){window.location.href='unsubscribe.html?host=" + host + "&consumer=" + URL.encode(s) + "';}\">Unsubscribe</button>");

+                    rows.add(row);

+                }

+            }

+            return new Page("<a href=\"hosts.html\">Hosts</a> &gt; " + NetUtils.getHostName(host) + "/" + host + " &gt; <a href=\"providers.html?host=" + host + "\">Providers</a> | Consumers", "Consumers (" + rows.size() + ")",

+                    new String[] { "Consumer URL:", "Unsubscribe" }, rows);

+        } else if (application != null && application.length() > 0) {

+            List<List<String>> rows = new ArrayList<List<String>>();

+            List<URL> consumers = RegistryContainer.getInstance().getConsumersByApplication(application);

+            if (consumers != null && consumers.size() > 0) {

+                for (URL u : consumers) {

+                    List<String> row = new ArrayList<String>();

+                    String s = u.toFullString();

+                    row.add(s.replace("&", "&amp;"));

+                    row.add("<button onclick=\"if(confirm('Confirm unsubscribe consumer?')){window.location.href='unsubscribe.html?application=" + application + "&consumer=" + URL.encode(s) + "';}\">Unsubscribe</button>");

+                    rows.add(row);

+                }

+            }

+            return new Page("<a href=\"applications.html\">Applications</a> &gt; " + application + " &gt; <a href=\"providers.html?application=" + application + "\">Providers</a> | Consumers | <a href=\"dependencies.html?application=" + application + "\">Depends On</a> | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>", "Consumers (" + rows.size() + ")",

+                    new String[] { "Consumer URL:", "Unsubscribe" }, rows);

+        } else {

+            throw new IllegalArgumentException("Please input service or host or application parameter.");

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/DependenciesPageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/DependenciesPageHandler.java
new file mode 100644
index 0000000..103b737
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/DependenciesPageHandler.java
@@ -0,0 +1,89 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.simple.RegistryContainer;

+

+/**

+ * DependenciesPageHandler

+ * 

+ * @author william.liangf

+ */

+public class DependenciesPageHandler implements PageHandler {

+    

+    public Page handle(URL url) {

+        String application = url.getParameter("application");

+        if (application == null || application.length() == 0) {

+            throw new IllegalArgumentException("Please input application parameter.");

+        }

+        boolean reverse = url.getParameter("reverse", false);

+        List<List<String>> rows = new ArrayList<List<String>>();

+        Set<String> directly = RegistryContainer.getInstance().getDependencies(application, reverse);

+        Set<String> indirectly = new HashSet<String>();

+        appendDependency(rows, reverse, application, 0, new HashSet<String>(), indirectly);

+        indirectly.remove(application);

+        return new Page("<a href=\"applications.html\">Applications</a> &gt; " + application + 

+                " &gt; <a href=\"providers.html?application=" + application + "\">Providers</a> | <a href=\"consumers.html?application=" + application + "\">Consumers</a> | " +

+                (reverse ? "<a href=\"dependencies.html?application=" + application + "\">Depends On</a> | Used By" 

+                        : "Depends On | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>"), (reverse ? "Used By" : "Depends On") + " (" + directly.size() + "/" + indirectly.size() + ")", new String[] { "Application Name:"}, rows);

+    }

+    

+    private void appendDependency(List<List<String>> rows, boolean reverse, String application, int level, Set<String> appended, Set<String> indirectly) {

+        List<String> row = new ArrayList<String>();

+        StringBuilder buf = new StringBuilder();

+        if (level > 0) {

+            for (int i = 0; i < level; i ++) {

+                buf.append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|");

+            }

+            buf.append(reverse ? "&lt;-- " : "--&gt; ");

+        }

+        boolean end = false;

+        if (level > 5) {

+            buf.append(" <font color=\"blue\">More...</font>");

+            end = true;

+        } else {

+            buf.append(application);

+            if (appended.contains(application)) {

+                buf.append(" <font color=\"red\">(Cycle)</font>");

+                end = true;

+            }

+        }

+        row.add(buf.toString());

+        rows.add(row);

+        if (end) {

+            return;

+        }

+        

+        appended.add(application);

+        indirectly.add(application);

+        Set<String> dependencies = RegistryContainer.getInstance().getDependencies(application, reverse);

+        if (dependencies != null && dependencies.size() > 0) {

+            for (String dependency : dependencies) {

+                appendDependency(rows, reverse, dependency, level + 1, appended, indirectly);

+            }

+        }

+        appended.remove(application);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/HostsPageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/HostsPageHandler.java
new file mode 100644
index 0000000..d298f9a
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/HostsPageHandler.java
@@ -0,0 +1,76 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.container.page.Menu;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.simple.RegistryContainer;

+

+/**

+ * HostsPageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "Hosts", desc = "Show provider and consumer hosts", order = 3000)

+public class HostsPageHandler implements PageHandler {

+    

+    public Page handle(URL url) {

+        List<List<String>> rows = new ArrayList<List<String>>();

+        Set<String> hosts = RegistryContainer.getInstance().getHosts();

+        int providersCount = 0;

+        int consumersCount = 0;

+        if (hosts != null && hosts.size() > 0) {

+            for (String host : hosts) {

+                List<String> row = new ArrayList<String>();

+                row.add(NetUtils.getHostName(host) + "/" + host);

+                

+                List<URL> providers = RegistryContainer.getInstance().getProvidersByHost(host);

+                List<URL> consumers = RegistryContainer.getInstance().getConsumersByHost(host);

+                

+                if (providers != null && providers.size() > 0

+                        || consumers != null && consumers.size() > 0) {

+                    URL provider = (providers != null && providers.size() > 0 ? providers.iterator().next() : consumers.iterator().next());

+                    row.add(provider.getParameter(Constants.APPLICATION_KEY, ""));

+                    row.add(provider.getParameter("owner", "") + (provider.hasParameter("organization") ?  " (" + provider.getParameter("organization") + ")" : ""));

+                } else {

+                    row.add("");

+                    row.add("");

+                }

+                

+                int proviedSize = providers == null ? 0 : providers.size();

+                providersCount += proviedSize;

+                row.add(proviedSize == 0 ? "<font color=\"blue\">No provider</font>" : "<a href=\"providers.html?host=" + host + "\">Providers(" + proviedSize + ")</a>");

+                

+                int consumersSize = consumers == null ? 0 : consumers.size();

+                consumersCount += consumersSize;

+                row.add(consumersSize == 0 ? "<font color=\"blue\">No consumer</font>" : "<a href=\"consumers.html?host=" + host + "\">Consumers(" + consumersSize + ")</a>");

+                

+                rows.add(row);

+            }

+        }

+        return new Page("Hosts", "Hosts (" + rows.size() + ")",

+                new String[] { "Host Name/IP:", "Application", "Owner", "Providers(" + providersCount + ")", "Consumers(" + consumersCount + ")" }, rows);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ProvidersPageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ProvidersPageHandler.java
new file mode 100644
index 0000000..7de660a
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ProvidersPageHandler.java
@@ -0,0 +1,89 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.simple.RegistryContainer;

+

+/**

+ * ProvidersPageHandler

+ * 

+ * @author william.liangf

+ */

+public class ProvidersPageHandler implements PageHandler {

+    

+    public Page handle(URL url) {

+        String service = url.getParameter("service");

+        String host = url.getParameter("host");

+        String application = url.getParameter("application");

+        if (service != null && service.length() > 0) {

+            List<List<String>> rows = new ArrayList<List<String>>();

+            List<URL> providers = RegistryContainer.getInstance().getProvidersByService(service);

+            if (providers != null && providers.size() > 0) {

+                for (URL u : providers) {

+                    List<String> row = new ArrayList<String>();

+                    String s = u.toFullString();

+                    row.add(s.replace("&", "&amp;"));

+                    row.add("<button onclick=\"if(confirm('Confirm unregister provider?')){window.location.href='unregister.html?service=" + service + "&provider=" + URL.encode(s) + "';}\">Unregister</button>");

+                    rows.add(row);

+                }

+            }

+            return new Page("<a href=\"services.html\">Services</a> &gt; " + service 

+                    + " &gt; Providers | <a href=\"consumers.html?service=" + service 

+                    + "\">Consumers</a> | <a href=\"statistics.html?service=" + service 

+                    + "\">Statistics</a> | <a href=\"charts.html?service=" + service 

+                    + "\">Charts</a>", "Providers (" + rows.size() + ")",

+                    new String[] { "Provider URL:", "Unregister" }, rows);

+        } else if (host != null && host.length() > 0) {

+            List<List<String>> rows = new ArrayList<List<String>>();

+            List<URL> providers = RegistryContainer.getInstance().getProvidersByHost(host);

+            if (providers != null && providers.size() > 0) {

+                for (URL u : providers) {

+                    List<String> row = new ArrayList<String>();

+                    String s = u.toFullString();

+                    row.add(s.replace("&", "&amp;"));

+                    row.add("<button onclick=\"if(confirm('Confirm unregister provider?')){window.location.href='unregister.html?host=" + host + "&provider=" + URL.encode(s) + "';}\">Unregister</button>");

+                    rows.add(row);

+                }

+            }

+            return new Page("<a href=\"hosts.html\">Hosts</a> &gt; " + NetUtils.getHostName(host) + "/" + host + " &gt; Providers | <a href=\"consumers.html?host=" + host + "\">Consumers</a>", "Providers (" + rows.size() + ")",

+                    new String[] { "Provider URL:", "Unregister" }, rows);

+        } else if (application != null && application.length() > 0) {

+            List<List<String>> rows = new ArrayList<List<String>>();

+            List<URL> providers = RegistryContainer.getInstance().getProvidersByApplication(application);

+            if (providers != null && providers.size() > 0) {

+                for (URL u : providers) {

+                    List<String> row = new ArrayList<String>();

+                    String s = u.toFullString();

+                    row.add(s.replace("&", "&amp;"));

+                    row.add("<button onclick=\"if(confirm('Confirm unregister provider?')){window.location.href='unregister.html?application=" + application + "&provider=" + URL.encode(s) + "';}\">Unregister</button>");

+                    rows.add(row);

+                }

+            }

+            return new Page("<a href=\"applications.html\">Applications</a> &gt; " + application + " &gt; Providers | <a href=\"consumers.html?application=" + application + "\">Consumers</a> | <a href=\"dependencies.html?application=" + application + "\">Depends On</a> | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>", "Providers (" + rows.size() + ")",

+                    new String[] { "Provider URL:", "Unregister" }, rows);

+        } else {

+            throw new IllegalArgumentException("Please input service or host or application parameter.");

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ServicesPageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ServicesPageHandler.java
new file mode 100644
index 0000000..cc0e589
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ServicesPageHandler.java
@@ -0,0 +1,73 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Menu;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.simple.RegistryContainer;

+

+/**

+ * ServicesPageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "Services", desc = "Show registered services.", order = 2000)

+public class ServicesPageHandler implements PageHandler {

+    

+    public Page handle(URL url) {

+        Set<String> services = RegistryContainer.getInstance().getServices();

+        List<List<String>> rows = new ArrayList<List<String>>();

+        int providerCount = 0;

+        int consumerCount = 0;

+        if (services != null && services.size() > 0) {

+            for (String service : services) {

+                List<URL> providers = RegistryContainer.getInstance().getProvidersByService(service);

+                int providerSize = providers == null ? 0 : providers.size();

+                providerCount += providerSize;

+                List<URL> consumers = RegistryContainer.getInstance().getConsumersByService(service);

+                int consumerSize = consumers == null ? 0 : consumers.size();

+                consumerCount += consumerSize;

+                List<String> row = new ArrayList<String>();

+                row.add(service);

+                if (providerSize > 0 || consumerSize > 0) {

+                    if (providerSize > 0) {

+                        URL provider = providers.iterator().next();

+                        row.add(provider.getParameter(Constants.APPLICATION_KEY, ""));

+                        row.add(provider.getParameter("owner", "") + (provider.hasParameter("organization") ?  " (" + provider.getParameter("organization") + ")" : ""));

+                    } else {

+                        row.add("");

+                        row.add("");

+                    }

+                    row.add(providerSize == 0 ? "<font color=\"red\">No provider</a>" : "<a href=\"providers.html?service=" + service + "\">Providers(" + providerSize + ")</a>");

+                    row.add(consumerSize == 0 ? "<font color=\"blue\">No consumer</a>" : "<a href=\"consumers.html?service=" + service + "\">Consumers(" + consumerSize + ")</a>");

+                    row.add("<a href=\"statistics.html?service=" + service + "\">Statistics</a>");

+                    row.add("<a href=\"charts.html?service=" + service + "\">Charts</a>");

+                    rows.add(row);

+                }

+            }

+        }

+        return new Page("Services", "Services (" + rows.size() + ")",

+                new String[] { "Service Name:", "Application", "Owner", "Providers(" + providerCount + ")", "Consumers(" + consumerCount + ")", "Statistics", "Charts" }, rows);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/StatisticsPageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/StatisticsPageHandler.java
new file mode 100644
index 0000000..d235dd5
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/StatisticsPageHandler.java
@@ -0,0 +1,168 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import java.io.File;

+import java.text.SimpleDateFormat;

+import java.util.ArrayList;

+import java.util.Date;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.MonitorService;

+import com.alibaba.dubbo.monitor.simple.CountUtils;

+import com.alibaba.dubbo.monitor.simple.SimpleMonitorService;

+

+/**

+ * StatisticsPageHandler

+ * 

+ * @author william.liangf

+ */

+public class StatisticsPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        String service = url.getParameter("service");

+        if (service == null || service.length() == 0) {

+            throw new IllegalArgumentException("Please input service parameter.");

+        }

+        String date = url.getParameter("date");

+        if (date == null || date.length() == 0) {

+            date = new SimpleDateFormat("yyyyMMdd").format(new Date());

+        }

+        String expand = url.getParameter("expand");

+        List<List<String>> rows = new ArrayList<List<String>>();

+        String directory = SimpleMonitorService.getInstance().getStatisticsDirectory();

+        String filename = directory + "/" + date + "/" + service;

+        File serviceDir = new File(filename);

+        if (serviceDir.exists()) {

+            File[] methodDirs = serviceDir.listFiles();

+            for (File methodDir : methodDirs) {

+                long[] statistics = newStatistics();

+                Map<String, long[]> expandMap = new HashMap<String, long[]>();

+                File[] consumerDirs = methodDir.listFiles();

+                for (File consumerDir : consumerDirs) {

+                    long[] expandStatistics = null;

+                    if (MonitorService.CONSUMER.equals(expand)) {

+                        expandStatistics = newStatistics();

+                        expandMap.put(consumerDir.getName(), expandStatistics);

+                    }

+                    File[] providerDirs = consumerDir.listFiles();

+                    for (File providerDir : providerDirs) {

+                        if (MonitorService.PROVIDER.equals(expand)) {

+                            expandStatistics = newStatistics();

+                            expandMap.put(providerDir.getName(), expandStatistics);

+                        }

+                        appendStatistics(providerDir, statistics);

+                        if (expandStatistics != null) {

+                            appendStatistics(providerDir, expandStatistics);

+                        }

+                    }

+                }

+                rows.add(toRow(methodDir.getName(), statistics));

+                if (expandMap != null && expandMap.size() > 0) {

+                    for (Map.Entry<String, long[]> entry : expandMap.entrySet()) {

+                        String node = MonitorService.CONSUMER.equals(expand) ? "&lt;--" : "--&gt;";

+                        rows.add(toRow(" &nbsp;&nbsp;&nbsp;&nbsp; |" + node + " " + entry.getKey(), entry.getValue()));

+                    }

+                }

+            }

+        }

+        StringBuilder nav = new StringBuilder();

+        nav.append("<a href=\"services.html\">Services</a> &gt; ");

+        nav.append(service);

+        nav.append(" &gt; <a href=\"providers.html?service=");

+        nav.append(service);

+        nav.append("\">Providers</a> | <a href=\"consumers.html?service=");

+        nav.append(service);

+        nav.append("\">Consumers</a> | Statistics | <a href=\"charts.html?service=");

+        nav.append(service);

+        nav.append("&date=");

+        nav.append(date);

+        nav.append("\">Charts</a> &gt; <input type=\"text\" style=\"width: 65px;\" name=\"date\" value=\"");

+        nav.append(date);

+        nav.append("\" onkeyup=\"if (event.keyCode == 10 || event.keyCode == 13) {window.location.href='statistics.html?service=");

+        nav.append(service);

+        if (expand != null && expand.length() > 0) {

+            nav.append("&expand=");

+            nav.append(expand);

+        }

+        nav.append("&date=' + this.value;}\" /> &gt; ");

+        if (! MonitorService.PROVIDER.equals(expand) && ! MonitorService.CONSUMER.equals(expand)) {

+            nav.append("Summary");

+        } else {

+            nav.append("<a href=\"statistics.html?service=");

+            nav.append(service);

+            nav.append("&date=");

+            nav.append(date);

+            nav.append("\">Summary</a>");

+        }

+        if (MonitorService.PROVIDER.equals(expand)) {

+            nav.append(" | +Provider");

+        } else {

+            nav.append(" | <a href=\"statistics.html?service=");

+            nav.append(service);

+            nav.append("&date=");

+            nav.append(date);

+            nav.append("&expand=provider\">+Provider</a>");

+        }

+        if (MonitorService.CONSUMER.equals(expand)) {

+            nav.append(" | +Consumer");

+        } else {

+            nav.append(" | <a href=\"statistics.html?service=");

+            nav.append(service);

+            nav.append("&date=");

+            nav.append(date);

+            nav.append("&expand=consumer\">+Consumer</a>");

+        }

+        return new Page(nav.toString(), "Statistics (" + rows.size() + ")",

+                new String[] { "Method:", "Success", "Failure", "Avg Elapsed (ms)",

+                        "Max Elapsed (ms)", "Max Concurrent" }, rows);

+    }

+    

+    private long[] newStatistics() {

+        return new long[10];

+    }

+    

+    private void appendStatistics(File providerDir, long[] statistics) {

+        statistics[0] += CountUtils.sum(new File(providerDir, MonitorService.CONSUMER + "." + MonitorService.SUCCESS));

+        statistics[1] += CountUtils.sum(new File(providerDir, MonitorService.PROVIDER + "." + MonitorService.SUCCESS));

+        statistics[2] += CountUtils.sum(new File(providerDir, MonitorService.CONSUMER + "." + MonitorService.FAILURE));

+        statistics[3] += CountUtils.sum(new File(providerDir, MonitorService.PROVIDER + "." + MonitorService.FAILURE));

+        statistics[4] += CountUtils.sum(new File(providerDir, MonitorService.CONSUMER + "." + MonitorService.ELAPSED));

+        statistics[5] += CountUtils.sum(new File(providerDir, MonitorService.PROVIDER + "." + MonitorService.ELAPSED));

+        statistics[6] = Math.max(statistics[6], CountUtils.max(new File(providerDir, MonitorService.CONSUMER + "." + MonitorService.MAX_ELAPSED)));

+        statistics[7] = Math.max(statistics[7], CountUtils.max(new File(providerDir, MonitorService.PROVIDER + "." + MonitorService.MAX_ELAPSED)));

+        statistics[8] = Math.max(statistics[8], CountUtils.max(new File(providerDir, MonitorService.CONSUMER + "." + MonitorService.MAX_CONCURRENT)));

+        statistics[9] = Math.max(statistics[9], CountUtils.max(new File(providerDir, MonitorService.PROVIDER + "." + MonitorService.MAX_CONCURRENT)));

+    }

+    

+    private List<String> toRow(String name, long[] statistics) {

+        List<String> row = new ArrayList<String>();

+        row.add(name);

+        row.add(String.valueOf(statistics[0]) + " --&gt; " + String.valueOf(statistics[1]));

+        row.add(String.valueOf(statistics[2]) + " --&gt; " + String.valueOf(statistics[3]));

+        row.add(String.valueOf(statistics[0] == 0 ? 0 : statistics[4] / statistics[0]) 

+                + " --&gt; " + String.valueOf(statistics[1] == 0 ? 0 : statistics[5] / statistics[1]));

+        row.add(String.valueOf(statistics[6]) + " --&gt; " + String.valueOf(statistics[7]));

+        row.add(String.valueOf(statistics[8]) + " --&gt; " + String.valueOf(statistics[9]));

+        return row;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnregisterPageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnregisterPageHandler.java
new file mode 100644
index 0000000..bc35cfd
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnregisterPageHandler.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.simple.RegistryContainer;

+

+/**

+ * UnregisterPageHandler

+ * 

+ * @author william.liangf

+ */

+public class UnregisterPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        String provider = url.getParameterAndDecoded("provider");

+        if (provider == null || provider.length() == 0) {

+            throw new IllegalArgumentException("Please input provider parameter.");

+        }

+        URL providerUrl = URL.valueOf(provider);

+        RegistryContainer.getInstance().getRegistry().unregister(providerUrl, null);

+        String parameter;

+        if (url.hasParameter("service")) {

+            parameter = "service=" + url.getParameter("service");

+        } else if (url.hasParameter("host")) {

+            parameter = "host=" + url.getParameter("host");

+        } else if (url.hasParameter("application")) {

+            parameter = "application=" + url.getParameter("application");

+        } else {

+            parameter = "service=" + providerUrl.getServiceInterface();

+        }

+        return new Page("<script type=\"text/javascript\">window.location.href=\"providers.html?" + parameter + "\";</script>");

+    }

+

+}

diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnsubscribePageHandler.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnsubscribePageHandler.java
new file mode 100644
index 0000000..456ae47
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnsubscribePageHandler.java
@@ -0,0 +1,63 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple.pages;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.monitor.simple.RegistryContainer;

+import com.alibaba.dubbo.registry.NotifyListener;

+

+/**

+ * UnsubscribePageHandler

+ * 

+ * @author william.liangf

+ */

+public class UnsubscribePageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        String consumer = url.getParameterAndDecoded("consumer");

+        if (consumer == null || consumer.length() == 0) {

+            throw new IllegalArgumentException("Please input consumer parameter.");

+        }

+        URL consumerUrl = URL.valueOf(consumer);

+        RegistryContainer.getInstance().getRegistry().unsubscribe(consumerUrl, NotifyListenerAdapter.NOTIFY_LISTENER);

+        String parameter;

+        if (url.hasParameter("service")) {

+            parameter = "service=" + url.getParameter("service");

+        } else if (url.hasParameter("host")) {

+            parameter = "host=" + url.getParameter("host");

+        } else if (url.hasParameter("application")) {

+            parameter = "application=" + url.getParameter("application");

+        } else {

+            parameter = "service=" + consumerUrl.getServiceInterface();

+        }

+        return new Page("<script type=\"text/javascript\">window.location.href=\"consumers.html?" + parameter + "\";</script>");

+    }

+    

+    private static class NotifyListenerAdapter implements NotifyListener {

+        

+        public static final NotifyListener NOTIFY_LISTENER = new NotifyListenerAdapter();

+        

+        private NotifyListenerAdapter() {}

+

+        public void notify(List<URL> urls) {}

+        

+    }

+

+}

diff --git a/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container b/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..139ca06
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
@@ -0,0 +1 @@
+registry=com.alibaba.dubbo.monitor.simple.RegistryContainer
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler b/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..23404cc
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,10 @@
+services=com.alibaba.dubbo.monitor.simple.pages.ServicesPageHandler

+providers=com.alibaba.dubbo.monitor.simple.pages.ProvidersPageHandler

+consumers=com.alibaba.dubbo.monitor.simple.pages.ConsumersPageHandler

+statistics=com.alibaba.dubbo.monitor.simple.pages.StatisticsPageHandler

+charts=com.alibaba.dubbo.monitor.simple.pages.ChartsPageHandler

+applications=com.alibaba.dubbo.monitor.simple.pages.ApplicationsPageHandler

+dependencies=com.alibaba.dubbo.monitor.simple.pages.DependenciesPageHandler

+hosts=com.alibaba.dubbo.monitor.simple.pages.HostsPageHandler

+unregister=com.alibaba.dubbo.monitor.simple.pages.UnregisterPageHandler

+unsubscribe=com.alibaba.dubbo.monitor.simple.pages.UnsubscribePageHandler
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/resources/META-INF/spring/dubbo-monitor-simple.xml b/dubbo-monitor-simple/src/main/resources/META-INF/spring/dubbo-monitor-simple.xml
new file mode 100644
index 0000000..f6bfef2
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/resources/META-INF/spring/dubbo-monitor-simple.xml
@@ -0,0 +1,40 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+	

+	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

+        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />

+        <property name="location" value="classpath:dubbo.properties" />

+    </bean>

+	

+	<dubbo:application name="${dubbo.application.name}" owner="${dubbo.application.owner}" />

+	

+	<dubbo:registry address="${dubbo.registry.address}" />

+	

+	<dubbo:protocol name="dubbo" port="${dubbo.protocol.port}" />

+	

+	<dubbo:service interface="com.alibaba.dubbo.monitor.MonitorService" ref="monitorService" delay="-1" />

+	

+	<bean id="monitorService" class="com.alibaba.dubbo.monitor.simple.SimpleMonitorService">

+		<property name="statisticsDirectory" value="${dubbo.statistics.directory}" />

+		<property name="chartsDirectory" value="${dubbo.charts.directory}" />

+	</bean>

+	

+</beans>
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitor.java b/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitor.java
new file mode 100644
index 0000000..0ed6b31
--- /dev/null
+++ b/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitor.java
@@ -0,0 +1,24 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple;

+

+public class SimpleMonitor {

+    

+    public static void main(String[] args) {

+        com.alibaba.dubbo.container.Main.main(args);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorServiceTest.java b/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorServiceTest.java
new file mode 100644
index 0000000..5fdd141
--- /dev/null
+++ b/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorServiceTest.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.simple;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+

+/**

+ * SimpleMonitorServiceTest

+ * 

+ * @author william.liangf

+ */

+public class SimpleMonitorServiceTest {

+    

+    @Test

+    public void testMonitor() {

+        new SimpleMonitorService().count(new URL("dubbo", NetUtils.getLocalHost(), 0));

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/test/resources/dubbo.properties b/dubbo-monitor-simple/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..8ee345a
--- /dev/null
+++ b/dubbo-monitor-simple/src/test/resources/dubbo.properties
@@ -0,0 +1,29 @@
+##

+# Copyright 1999-2011 Alibaba Group.

+#  

+# 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.

+##

+dubbo.container=log4j,spring,registry,jetty

+dubbo.application.name=simple-monitor

+dubbo.application.owner=

+dubbo.registry.address=multicast://224.5.6.7:1234

+#dubbo.registry.address=zookeeper://127.0.0.1:2181

+#dubbo.registry.address=redis://127.0.0.1:6379

+#dubbo.registry.address=dubbo://127.0.0.1:9090

+dubbo.protocol.port=7070

+dubbo.jetty.port=8080

+dubbo.jetty.directory=${user.home}/monitor

+dubbo.charts.directory=${dubbo.jetty.directory}/charts

+dubbo.statistics.directory=${user.home}/monitor/statistics

+#dubbo.log4j.file=logs/dubbo-demo-consumer.log

+#dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/test/resources/log4j.xml b/dubbo-monitor-simple/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-monitor-simple/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">

+	<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

+		<layout class="org.apache.log4j.PatternLayout">

+			<param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />

+		</layout>

+	</appender>

+	<root>

+		<level value="INFO" />

+		<appender-ref ref="CONSOLE" />

+	</root>

+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-monitor/pom.xml b/dubbo-monitor/pom.xml
new file mode 100644
index 0000000..ab0b483
--- /dev/null
+++ b/dubbo-monitor/pom.xml
@@ -0,0 +1,38 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+	<modelVersion>4.0.0</modelVersion>

+	<parent>

+		<groupId>com.alibaba</groupId>

+		<artifactId>dubbo-parent</artifactId>

+		<version>2.1.1</version>

+	</parent>

+	<artifactId>dubbo-monitor</artifactId>

+	<packaging>jar</packaging>

+	<name>Dubbo Monitor Module</name>

+	<description>The monitor module of dubbo project</description>

+	<properties>

+		<skip_maven_deploy>true</skip_maven_deploy>

+	</properties>

+	<dependencies>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+	</dependencies>

+</project>
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/Monitor.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/Monitor.java
new file mode 100644
index 0000000..0c12a53
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/Monitor.java
@@ -0,0 +1,28 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor;

+

+import com.alibaba.dubbo.common.Node;

+

+/**

+ * Monitor. (SPI, Prototype, ThreadSafe)

+ * 

+ * @see com.alibaba.dubbo.monitor.MonitorFactory#getMonitor(com.alibaba.dubbo.common.URL)

+ * @author william.liangf

+ */

+public interface Monitor extends Node, MonitorService {

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java
new file mode 100644
index 0000000..d61fb01
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java
@@ -0,0 +1,39 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * MonitorFactory. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@SPI("dubbo")

+public interface MonitorFactory {

+    

+    /**

+     * Create monitor.

+     * 

+     * @param url

+     * @return monitor

+     */

+    @Adaptive("protocol")

+    Monitor getMonitor(URL url);

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorService.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorService.java
new file mode 100644
index 0000000..7d2cd52
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorService.java
@@ -0,0 +1,70 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor;

+

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * MonitorService. (SPI, Prototype, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public interface MonitorService {

+    

+    String APPLICATION = "application";

+    

+    String INTERFACE = "interface";

+

+    String METHOD = "method";

+

+    String GROUP = "group";

+

+    String VERSION = "version";

+

+    String CONSUMER = "consumer";

+

+    String PROVIDER = "provider";

+    

+    String TIMESTAMP = "timestamp";

+

+    String SUCCESS = "success";

+

+    String FAILURE = "failure";

+    

+    String INPUT = "input";

+

+    String OUTPUT = "output";

+

+    String ELAPSED = "elapsed";

+

+    String CONCURRENT = "concurrent";

+

+    String MAX_INPUT = "max.input";

+

+    String MAX_OUTPUT = "max.output";

+

+    String MAX_ELAPSED = "max.elapsed";

+

+    String MAX_CONCURRENT = "max.concurrent";

+

+    /**

+     * count.

+     * 

+     * @param statistics

+     */

+    void count(URL statistics);

+    

+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/AbstractMonitorFactroy.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/AbstractMonitorFactroy.java
new file mode 100644
index 0000000..82198da
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/AbstractMonitorFactroy.java
@@ -0,0 +1,67 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.support;

+

+import java.util.Collection;

+import java.util.Collections;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.locks.ReentrantLock;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.monitor.Monitor;

+import com.alibaba.dubbo.monitor.MonitorFactory;

+

+/**

+ * AbstractMonitorFactroy. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractMonitorFactroy implements MonitorFactory {

+

+    // 注册中心获取过程锁

+    private static final ReentrantLock LOCK = new ReentrantLock();

+    

+    // 注册中心集合 Map<RegistryAddress, Registry>

+    private static final Map<String, Monitor> MONITORS = new ConcurrentHashMap<String, Monitor>();

+

+    public static Collection<Monitor> getMonitors() {

+        return Collections.unmodifiableCollection(MONITORS.values());

+    }

+

+    public Monitor getMonitor(URL url) {

+        LOCK.lock();

+        try {

+            String uri = url.toIdentityString();

+            Monitor monitor = MONITORS.get(uri);

+            if (monitor != null) {

+                return monitor;

+            }

+            monitor = createMonitor(url);

+            if (monitor == null) {

+                throw new IllegalStateException("Can not create monitor " + url);

+            }

+            MONITORS.put(uri, monitor);

+            return monitor;

+        } finally {

+            // 释放锁

+            LOCK.unlock();

+        }

+    }

+

+    protected abstract Monitor createMonitor(URL url);

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/MonitorFilter.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/MonitorFilter.java
new file mode 100644
index 0000000..f206890
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/MonitorFilter.java
@@ -0,0 +1,129 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.support;

+

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.monitor.Monitor;

+import com.alibaba.dubbo.monitor.MonitorFactory;

+import com.alibaba.dubbo.monitor.MonitorService;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+ 

+/**

+ * MonitorFilter. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})

+public class MonitorFilter implements Filter {

+

+    private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class);

+    

+    private final ConcurrentMap<String, AtomicInteger> concurrents = new ConcurrentHashMap<String, AtomicInteger>();

+    

+    private MonitorFactory monitorFactory;

+    

+    public void setMonitorFactory(MonitorFactory monitorFactory) {

+        this.monitorFactory = monitorFactory;

+    }

+    

+    // 调用过程拦截

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) {

+            RpcContext context = RpcContext.getContext(); // 提供方必须在invoke()之前获取context信息

+            long start = System.currentTimeMillis(); // 记录起始时间戮

+            getConcurrent(invoker, invocation).incrementAndGet(); // 并发计数

+            try {

+                Result result = invoker.invoke(invocation); // 让调用链往下执行

+                collect(invoker, invocation, context, start, false);

+                return result;

+            } catch (RpcException e) {

+                collect(invoker, invocation, context, start, true);

+                throw e;

+            } finally {

+                getConcurrent(invoker, invocation).decrementAndGet(); // 并发计数

+            }

+        } else {

+            return invoker.invoke(invocation);

+        }

+    }

+    

+    // 信息采集

+    private void collect(Invoker<?> invoker, Invocation invocation, RpcContext context, long start, boolean error) {

+        try {

+            // ---- 服务信息获取 ----

+            long elapsed = System.currentTimeMillis() - start; // 计算调用耗时

+            int concurrent = getConcurrent(invoker, invocation).get(); // 当前并发数

+            String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);

+            String service = invoker.getInterface().getName(); // 获取服务名称

+            String method = invocation.getMethodName(); // 获取方法名

+            URL url = URL.valueOf(invoker.getUrl().getParameterAndDecoded(Constants.MONITOR_KEY));

+            Monitor monitor = monitorFactory.getMonitor(url);

+            // ---- 服务提供方监控 ----

+            String server = context.getLocalAddressString(); // 本地提供方地址

+            if (invoker.getUrl().getAddress().equals(server)) {

+                monitor.count(new URL(invoker.getUrl().getProtocol(), context.getRemoteHost(), 0, service + "/" + method)

+                        .addParameters(MonitorService.APPLICATION, application, 

+                                MonitorService.INTERFACE, service, 

+                                MonitorService.METHOD, method,

+                                MonitorService.PROVIDER, NetUtils.getLocalHost() + ":" + context.getLocalPort(),

+                                error ? MonitorService.FAILURE : MonitorService.SUCCESS, String.valueOf(1),

+                                MonitorService.ELAPSED, String.valueOf(elapsed),

+                                MonitorService.CONCURRENT, String.valueOf(concurrent)));

+            }

+            // ---- 服务消费方监控 ----

+            context = RpcContext.getContext(); // 消费方必须在invoke()之后获取context信息

+            server = context.getRemoteAddressString(); // 远程提供方地址

+            if (invoker.getUrl().getAddress().equals(server)) {

+                monitor.count(new URL(invoker.getUrl().getProtocol(), context.getRemoteHost(), context.getRemotePort(), service + "/" + method)

+                        .addParameters(MonitorService.APPLICATION, application, 

+                                MonitorService.INTERFACE, service, 

+                                MonitorService.METHOD, method,

+                                MonitorService.CONSUMER, NetUtils.getLocalHost(),

+                                error ? MonitorService.FAILURE : MonitorService.SUCCESS, String.valueOf(1),

+                                MonitorService.ELAPSED, String.valueOf(elapsed),

+                                MonitorService.CONCURRENT, String.valueOf(concurrent)));

+            }

+        } catch (Throwable t) {

+            logger.error("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t);

+        }

+    }

+    

+    // 获取并发计数器

+    private AtomicInteger getConcurrent(Invoker<?> invoker, Invocation invocation) {

+        String key = invoker.getInterface().getName() + "." + invocation.getMethodName();

+        AtomicInteger concurrent = concurrents.get(key);

+        if (concurrent == null) {

+            concurrents.putIfAbsent(key, new AtomicInteger());

+            concurrent = concurrents.get(key);

+        }

+        return concurrent;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-monitor/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..832b9cd
--- /dev/null
+++ b/dubbo-monitor/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+monitor=com.alibaba.dubbo.monitor.support.MonitorFilter
\ No newline at end of file
diff --git a/dubbo-monitor/src/test/java/com/alibaba/dubbo/monitor/support/MonitorFilterTest.java b/dubbo-monitor/src/test/java/com/alibaba/dubbo/monitor/support/MonitorFilterTest.java
new file mode 100644
index 0000000..d2e1870
--- /dev/null
+++ b/dubbo-monitor/src/test/java/com/alibaba/dubbo/monitor/support/MonitorFilterTest.java
@@ -0,0 +1,112 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.monitor.support;

+

+import java.io.UnsupportedEncodingException;

+import java.net.URLEncoder;

+

+import junit.framework.Assert;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.monitor.Monitor;

+import com.alibaba.dubbo.monitor.MonitorFactory;

+import com.alibaba.dubbo.monitor.MonitorService;

+import com.alibaba.dubbo.monitor.support.MonitorFilter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+

+/**

+ * MonitorFilterTest

+ * 

+ * @author william.liangf

+ */

+public class MonitorFilterTest {

+

+    private volatile URL lastStatistics;

+    

+    private volatile Invocation lastInvocation;

+    

+    private final Invoker<MonitorService> serviceInvoker = new Invoker<MonitorService>() {

+        public Class<MonitorService> getInterface() {

+            return MonitorService.class;

+        }

+        public URL getUrl() {

+            try {

+                return URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880?" + Constants.APPLICATION_KEY + "=abc&" + Constants.MONITOR_KEY + "=" + URLEncoder.encode("dubbo://" + NetUtils.getLocalHost() + ":7070", "UTF-8"));

+            } catch (UnsupportedEncodingException e) {

+                throw new IllegalStateException(e.getMessage(), e);

+            }

+        }

+        public boolean isAvailable() {

+            return false;

+        }

+        public Result invoke(Invocation invocation) throws RpcException {

+            lastInvocation = invocation;

+            return null;

+        }

+        public void destroy() {

+        }

+    };

+    

+    private MonitorFactory monitorFactory = new MonitorFactory() {

+        public Monitor getMonitor(final URL url) {

+            return new Monitor() {

+                public URL getUrl() {

+                    return url;

+                }

+                public boolean isAvailable() {

+                    return true;

+                }

+                public void destroy() {

+                }

+                public void count(URL statistics) {

+                    MonitorFilterTest.this.lastStatistics = statistics;

+                }

+            };

+        }

+    };

+    

+    @Test

+    public void testFilter() throws Exception {

+        MonitorFilter monitorFilter = new MonitorFilter();

+        monitorFilter.setMonitorFactory(monitorFactory);

+        Invocation invocation = new RpcInvocation("aaa", new Class<?>[0], new Object[0]);

+        RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);

+        monitorFilter.invoke(serviceInvoker, invocation);

+        while (lastStatistics == null) {

+            Thread.sleep(10);

+        }

+        Assert.assertEquals("abc", lastStatistics.getParameter(MonitorService.APPLICATION));

+        Assert.assertEquals(MonitorService.class.getName(), lastStatistics.getParameter(MonitorService.INTERFACE));

+        Assert.assertEquals("aaa", lastStatistics.getParameter(MonitorService.METHOD));

+        Assert.assertEquals(NetUtils.getLocalHost() + ":20880", lastStatistics.getAddress());

+        Assert.assertEquals(NetUtils.getLocalHost(), lastStatistics.getParameter(MonitorService.CONSUMER));

+        Assert.assertEquals(null, lastStatistics.getParameter(MonitorService.PROVIDER));

+        Assert.assertEquals(1, lastStatistics.getParameter(MonitorService.SUCCESS, 0));

+        Assert.assertEquals(0, lastStatistics.getParameter(MonitorService.FAILURE, 0));

+        Assert.assertEquals(1, lastStatistics.getParameter(MonitorService.CONCURRENT, 0));

+        Assert.assertEquals(invocation, lastInvocation);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-default/pom.xml b/dubbo-registry-default/pom.xml
new file mode 100644
index 0000000..86080eb
--- /dev/null
+++ b/dubbo-registry-default/pom.xml
@@ -0,0 +1,56 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-registry-default</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Default Registry Module</name>
+	<description>The default registry module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-registry</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-rpc-default</artifactId>
+			<version>${project.parent.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting-netty</artifactId>
+			<version>${project.parent.version}</version>
+			<scope>test</scope>
+		</dependency>

+		<dependency>

+			<groupId>org.apache.bsf</groupId>

+			<artifactId>bsf-api</artifactId>

+			<scope>test</scope>

+			<optional>true</optional>

+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java b/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
new file mode 100644
index 0000000..d70e833
--- /dev/null
+++ b/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
@@ -0,0 +1,149 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.dubbo;

+

+import java.util.List;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.locks.ReentrantLock;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.RegistryService;

+import com.alibaba.dubbo.registry.support.FailbackRegistry;

+import com.alibaba.dubbo.rpc.Invoker;

+

+/**

+ * DubboRegistry

+ * 

+ * @author william.liangf

+ */

+public class DubboRegistry extends FailbackRegistry {

+

+    private final static Logger logger = LoggerFactory.getLogger(DubboRegistry.class); 

+

+    // 重连检测周期3秒(单位毫秒)

+    private static final int RECONNECT_PERIOD_DEFAULT = 3 * 1000;

+    

+    // 定时任务执行器

+    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryReconnectTimer", true));

+

+    // 重连定时器,定时检查连接是否可用,不可用时,无限次重连

+    private final ScheduledFuture<?> reconnectFuture;

+

+    // 客户端获取过程锁,锁定客户端实例的创建过程,防止重复的客户端

+    private final ReentrantLock clientLock = new ReentrantLock();

+    

+    private final Invoker<RegistryService> registryInvoker;

+    

+    private final RegistryService registryService;

+    

+    public DubboRegistry(Invoker<RegistryService> registryInvoker, RegistryService registryService) {

+        super(registryInvoker.getUrl());

+        this.registryInvoker = registryInvoker;

+        this.registryService = registryService;

+        // 启动重连定时器

+        int reconnectPeriod = registryInvoker.getUrl().getParameter(Constants.REGISTRY_RECONNECT_PERIOD_KEY, RECONNECT_PERIOD_DEFAULT);

+        reconnectFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {

+            public void run() {

+                // 检测并连接注册中心

+                try {

+                    connect();

+                } catch (Throwable t) { // 防御性容错

+                    logger.error("Unexpected error occur at reconnect, cause: " + t.getMessage(), t);

+                }

+            }

+        }, reconnectPeriod, reconnectPeriod, TimeUnit.MILLISECONDS);

+    }

+

+    protected final void connect() {

+        try {

+            // 检查是否已连接

+            if (isAvailable()) {

+                return;

+            }

+            if (logger.isInfoEnabled()) {

+                logger.info("Reconnect to registry " + getUrl());

+            }

+            clientLock.lock();

+            try {

+                // 双重检查是否已连接

+                if (isAvailable()) {

+                    return;

+                }

+                recover();

+            } finally {

+                clientLock.unlock();

+            }

+        } catch (Throwable t) { // 忽略所有异常,等待下次重试

+             if (getUrl().getParameter(Constants.CHECK_KEY, true)) {

+                 if (t instanceof RuntimeException) {

+                     throw (RuntimeException) t;

+                 }

+                 throw new RuntimeException(t.getMessage(), t);

+             }

+             logger.error("Failed to connect to registry " + getUrl().getAddress() + " from provider/consumer " + NetUtils.getLocalHost() + " use dubbo " + Version.getVersion() + ", cause: " + t.getMessage(), t);

+        }

+    }

+    

+    public boolean isAvailable() {

+        if (registryInvoker == null)

+            return false;

+        return registryInvoker.isAvailable();

+    }

+    

+    public void destroy() {

+        super.destroy();

+        try {

+            // 取消重连定时器

+            if (! reconnectFuture.isCancelled()) {

+                reconnectFuture.cancel(true);

+            }

+        } catch (Throwable t) {

+            logger.warn("Failed to cancel reconnect timer", t);

+        }

+        registryInvoker.destroy();

+    }

+    

+    protected void doRegister(URL url) {

+        registryService.register(url, null);

+    }

+    

+    protected void doUnregister(URL url) {

+        registryService.unregister(url, null);

+    }

+

+    protected void doSubscribe(URL url, NotifyListener listener) {

+        registryService.subscribe(url, listener);

+    }

+    

+    protected void doUnsubscribe(URL url, NotifyListener listener) {

+        registryService.unsubscribe(url, listener);

+    }

+

+    public List<URL> lookup(URL url) {

+        return registryService.lookup(url);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.java b/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.java
new file mode 100644
index 0000000..54e9b32
--- /dev/null
+++ b/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.java
@@ -0,0 +1,101 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.dubbo;

+

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.HashSet;

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.bytecode.Wrapper;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.RegistryService;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+import com.alibaba.dubbo.registry.support.RegistryDirectory;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+

+/**

+ * DubboRegistryFactory

+ * 

+ * @author william.liangf

+ */

+public class DubboRegistryFactory extends AbstractRegistryFactory {

+    

+    private Protocol protocol;

+

+    public void setProtocol(Protocol protocol) {

+        this.protocol = protocol;

+    }

+

+    private ProxyFactory proxyFactory;

+

+    public void setProxyFactory(ProxyFactory proxyFactory) {

+        this.proxyFactory = proxyFactory;

+    }

+

+    private Cluster cluster;

+

+    public void setCluster(Cluster cluster) {

+        this.cluster = cluster;

+    }

+    

+    public Registry createRegistry(URL url) {

+        url = getRegistryURL(url);

+        List<URL> urls = new ArrayList<URL>();

+        urls.add(url.removeParameter(Constants.BACKUP_KEY));

+        String backup = url.getParameter(Constants.BACKUP_KEY);

+        if (backup != null && backup.length() > 0) {

+            String[] addresses = Constants.COMMA_SPLIT_PATTERN.split(backup);

+            for (String address : addresses) {

+                urls.add(url.setAddress(address));

+            }

+        }

+        RegistryDirectory<RegistryService> directory = new RegistryDirectory<RegistryService>(RegistryService.class, url.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName()).addParameterAndEncoded(Constants.REFER_KEY, url.toParameterString()));

+        Invoker<RegistryService> registryInvoker = cluster.join(directory);

+        RegistryService registryService = proxyFactory.getProxy(registryInvoker);

+        DubboRegistry registry = new DubboRegistry(registryInvoker, registryService);

+        directory.setRegistry(registry);

+        directory.setProtocol(protocol);

+        directory.notify(urls);

+        registryService.subscribe(new URL(Constants.SUBSCRIBE_PROTOCOL, NetUtils.getLocalHost(), 0, RegistryService.class.getName(), url.getParameters()), directory);

+        return registry;

+    }

+    

+    private static URL getRegistryURL(URL url) {

+        return url.setPath(RegistryService.class.getName())

+                .removeParameter(Constants.EXPORT_KEY).removeParameter(Constants.REFER_KEY)

+                .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())

+                .addParameter(Constants.CLUSTER_STICKY_KEY, "true")

+                .addParameter(Constants.LAZY_CONNECT_KEY, "true")

+                .addParameter(Constants.RECONNECT_KEY, "false")

+                .addParameterIfAbsent(Constants.TIMEOUT_KEY, "10000")

+                .addParameterIfAbsent(Constants.CALLBACK_INSTANCES_LIMIT_KEY, "10000")

+                .addParameterIfAbsent(Constants.CONNECT_TIMEOUT_KEY, "10000")

+                .addParameter(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(RegistryService.class).getDeclaredMethodNames())), ","))

+                //.addParameter(Constants.STUB_KEY, RegistryServiceStub.class.getName())

+                //.addParameter(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString()) //for event dispatch

+                //.addParameter(Constants.ON_DISCONNECT_KEY, "disconnect")

+                .addParameter("subscribe.1.callback", "true")

+                .addParameter("unsubscribe.1.callback", "false");

+    }

+}
\ No newline at end of file
diff --git a/dubbo-registry-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..55d739d
--- /dev/null
+++ b/dubbo-registry-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+dubbo=com.alibaba.dubbo.registry.dubbo.DubboRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/DemoService.java b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/DemoService.java
new file mode 100644
index 0000000..eda1c74
--- /dev/null
+++ b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/DemoService.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.dubbo;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+	void sayHello(String name);
+
+	int plus(int a,int b);
+}
\ No newline at end of file
diff --git a/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockChannel.java b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockChannel.java
new file mode 100644
index 0000000..19e1b5b
--- /dev/null
+++ b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockChannel.java
@@ -0,0 +1,111 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.dubbo;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+
+public class MockChannel implements ExchangeChannel {
+
+    final InetSocketAddress localAddress;
+
+    final InetSocketAddress remoteAddress;
+
+    public static boolean   closed = false;
+
+    public MockChannel(String localHostname, int localPort, String remoteHostName, int remotePort){
+        localAddress = new InetSocketAddress(localHostname, localPort);
+        remoteAddress = new InetSocketAddress(remoteHostName, remotePort);
+        closed = false;
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        return localAddress;
+    }
+
+    public InetSocketAddress getRemoteAddress() {
+        return remoteAddress;
+    }
+
+    public boolean isConnected() {
+        return true;
+    }
+
+    public void close() {
+        closed = true;
+    }
+
+    public void send(Object message) throws RemotingException {
+    }
+
+    public void close(int timeout) {
+    }
+
+    public URL getUrl() {
+        return null;
+    }
+
+    public ResponseFuture send(Object request, int timeout) throws RemotingException {
+        return null;
+    }
+
+    public ChannelHandler getChannelHandler() {
+        return null;
+    }
+
+    public ResponseFuture request(Object request) throws RemotingException {
+        return null;
+    }
+
+    public ResponseFuture request(Object request, int timeout) throws RemotingException {
+        return null;
+    }
+
+    public ExchangeHandler getExchangeHandler() {
+        return null;
+    }
+
+    public Object getAttribute(String key) {
+        return null;
+    }
+
+    public void setAttribute(String key, Object value) {
+
+    }
+
+    public boolean hasAttribute(String key) {
+        return false;
+    }
+
+    public boolean isClosed() {
+        return false;
+    }
+
+    public void removeAttribute(String key) {
+
+    }
+
+    public void send(Object message, boolean sent) throws RemotingException {
+
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.java b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.java
new file mode 100644
index 0000000..0d677d8
--- /dev/null
+++ b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.java
@@ -0,0 +1,264 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.dubbo;
+
+import java.net.InetSocketAddress;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Codec;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.exchange.ResponseCallback;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+import com.alibaba.dubbo.remoting.exchange.support.Replier;

+
+/**
+ * MockedClient
+ * 
+ * @author william.liangf
+ */
+public class MockedClient implements ExchangeClient {
+    
+    //private String host;
+
+    //private int port;
+    
+    private boolean connected;
+    
+    private Object received;
+
+    private Object sent;
+    
+    private Object invoked;
+    
+    private Replier<?> handler;
+ 
+    private InetSocketAddress address;
+    
+    private boolean closed = false;
+
+    //private ChannelListener listener;
+    
+    public MockedClient(String host, int port, boolean connected) {
+    	this(host, port, connected, null);
+    }
+
+    public MockedClient(String host, int port, boolean connected, Object received) {
+    	this.address = new InetSocketAddress(host, port);
+        this.connected = connected;
+        this.received = received;
+    }
+
+    public void open() {
+    }
+
+    public void close() {
+        this.closed = true;
+    }
+
+    public void send(Object msg) throws RemotingException {
+        this.sent = msg;
+    }
+
+    public ResponseFuture request(Object msg) throws RemotingException {
+        return request(msg, 0);
+    }
+
+    public ResponseFuture request(Object msg, int timeout) throws RemotingException {
+        this.invoked = msg;
+        return new ResponseFuture() {
+            public Object get() throws RemotingException {
+                return received;
+            }
+            public Object get(int timeoutInMillis) throws RemotingException {
+                return received;
+            }
+            public boolean isDone() {
+                return true;
+            }
+            public void setCallback(ResponseCallback callback) {
+            }
+        };
+    }
+
+    public void registerHandler(Replier<?> handler) {
+        this.handler = handler;
+    }
+
+    public void unregisterHandler(Replier<?> handler) {
+        //this.handler = null;
+    }
+
+    public void addChannelListener(ChannelHandler listener) {
+        //this.listener = listener;
+    }
+
+    public void removeChannelListener(ChannelHandler listener) {
+        //this.listener = null;
+    }
+
+    public boolean isConnected() {
+        return connected;
+    }
+
+    public Object getSent() {
+        return sent;
+    }
+
+    public Replier<?> getHandler() {
+        return handler;
+    }
+
+    public Object getInvoked() {
+        return invoked;
+    }
+
+	public InetSocketAddress getRemoteAddress() {
+		return address;
+	}
+
+	public String getName() {
+		return "mocked";
+	}
+
+	public InetSocketAddress getLocalAddress() {
+		return null;
+	}
+
+	public void setTimeout(int timeout) {
+	}
+
+	public int getTimeout() {
+		return 0;
+	}
+
+	public void close(int timeout) {

+	    close();
+	}
+
+	public boolean isOpen() {
+		return closed;
+	}
+
+	public Codec getCodec() {
+		return null;
+	}
+
+	public void setCodec(Codec codec) {
+	}
+
+    public void setHost(String host) {
+    }
+
+    public String getHost() {
+        return null;
+    }
+
+    public void setPort(int port) {
+    }
+
+    public int getPort() {
+        return 0;
+    }
+
+    public void setThreadCount(int threadCount) {
+    }
+
+    public int getThreadCount() {
+        return 0;
+    }
+
+    public URL getUrl() {
+        return null;
+    }
+
+    public Replier<?> getReceiver() {
+        return null;
+    }
+
+    public ChannelHandler getChannelHandler() {
+        return null;
+    }
+
+    public void reset(Map<String, String> parameters) {
+    }
+
+    public Channel getChannel() {
+        return this;
+    }
+
+    public ExchangeHandler getExchangeHandler() {
+        return null;
+    }
+
+    public void reconnect() throws RemotingException {
+    }
+
+    public Object getAttribute(String key) {
+        return null;
+    }
+
+    public void setAttribute(String key, Object value) {
+        
+    }
+
+    public boolean hasAttribute(String key) {
+        return false;
+    }
+
+    public boolean isClosed() {
+        return closed;
+    }
+
+    public void removeAttribute(String key) {
+        
+    }
+    /**
+     * @return the received
+     */
+    public Object getReceived() {
+        return received;
+    }
+
+    /**
+     * @param received the received to set
+     */
+    public void setReceived(Object received) {
+        this.received = received;
+    }
+
+    /**
+     * @param connected the connected to set
+     */
+    public void setConnected(boolean connected) {
+        this.connected = connected;
+    }
+
+    public void send(Object message, boolean sent) throws RemotingException {
+    }
+
+    public void reset(URL url) {
+    }

+

+    @Deprecated

+    public void reset(com.alibaba.dubbo.common.Parameters parameters) {

+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
new file mode 100644
index 0000000..cf88b98
--- /dev/null
+++ b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
@@ -0,0 +1,902 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.dubbo;

+

+import static org.junit.Assert.fail;

+

+import java.lang.reflect.Field;

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.CountDownLatch;

+

+import javax.script.ScriptEngineManager;

+

+import junit.framework.Assert;

+

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.LogUtil;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.RegistryFactory;

+import com.alibaba.dubbo.registry.support.RegistryDirectory;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.cluster.Router;

+import com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance;

+import com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;

+import com.alibaba.dubbo.rpc.cluster.router.ScriptRouter;

+import com.alibaba.dubbo.rpc.cluster.router.ScriptRouterFactory;

+

+@SuppressWarnings({ "rawtypes", "unchecked" })

+public class RegistryDirectoryTest {

+

+    RegistryFactory registryFactory         = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();

+    Protocol        protocol                = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+    String          service                 = DemoService.class.getName();

+    RpcInvocation   invocation              = new RpcInvocation();

+

+    URL             noMeaningUrl            = URL.valueOf("notsupport:/" + service+"?interface="+service);

+    URL             SERVICEURL              = URL.valueOf("dubbo://127.0.0.1:9091/" + service + "?lazy=true");

+    URL             SERVICEURL2             = URL.valueOf("dubbo://127.0.0.1:9092/" + service + "?lazy=true");

+    URL             SERVICEURL3             = URL.valueOf("dubbo://127.0.0.1:9093/" + service + "?lazy=true");

+    URL             SERVICEURL_DUBBO_NOPATH = URL.valueOf("dubbo://127.0.0.1:9092" + "?lazy=true");

+

+    @Before

+    public void setUp() {

+    }

+

+    private RegistryDirectory getRegistryDirectory(URL url) {

+        RegistryDirectory registryDirectory = new RegistryDirectory(URL.class, url);

+        registryDirectory.setProtocol(protocol);

+        // asert empty

+        List invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(0, invokers.size());

+        Assert.assertEquals(false, registryDirectory.isAvailable());

+        return registryDirectory;

+    }

+

+    private RegistryDirectory getRegistryDirectory() {

+        return getRegistryDirectory(noMeaningUrl);

+    }

+

+    @Test

+    public void test_Constructor_WithErrorParam() {

+        try {

+            new RegistryDirectory(null, null);

+            fail();

+        } catch (IllegalArgumentException e) {

+

+        }

+        try {

+            // null url

+            new RegistryDirectory(null, noMeaningUrl);

+            fail();

+        } catch (IllegalArgumentException e) {

+

+        }

+        try {

+            // no servicekey

+            new RegistryDirectory(RegistryDirectoryTest.class, URL.valueOf("dubbo://10.20.30.40:9090"));

+            fail();

+        } catch (IllegalArgumentException e) {

+

+        }

+    }

+

+    @Test

+    public void test_Constructor_CheckStatus() throws Exception {

+        URL url = URL.valueOf("notsupported://10.20.30.40/" + service + "?a=b").addParameterAndEncoded(Constants.REFER_KEY,

+                                                                                                       "foo=bar");

+        RegistryDirectory reg = getRegistryDirectory(url);

+        Field field = reg.getClass().getDeclaredField("queryMap");

+        field.setAccessible(true);

+        Map<String, String> queryMap = (Map<String, String>) field.get(reg);

+        Assert.assertEquals("bar", queryMap.get("foo"));

+        Assert.assertEquals(url.removeParameter(Constants.REFER_KEY).addParameter("foo", "bar"), reg.getUrl());

+    }

+

+    @Test

+    public void testNotified_Normal() {

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        test_Notified2invokers(registryDirectory);

+        test_Notified1invokers(registryDirectory);

+        test_Notified3invokers(registryDirectory);

+        testforbid(registryDirectory);

+    }

+    

+    /**

+     * 测试推送只有router的情况

+     */

+    @Test

+    public void testNotified_Normal_withRouters() {

+        LogUtil.start();

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        test_Notified1invokers(registryDirectory);

+        test_Notified_only_routes(registryDirectory);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+        Assert.assertTrue("notify no invoker urls ,should not error", LogUtil.checkNoError());

+        LogUtil.stop();

+        test_Notified2invokers(registryDirectory);

+        

+    }

+

+    @Test

+    public void testNotified_WithError() {

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        List<URL> serviceUrls = new ArrayList<URL>();

+        // ignore error log

+        URL badurl = URL.valueOf("notsupported://127.0.0.1/" + service);

+        serviceUrls.add(badurl);

+        serviceUrls.add(SERVICEURL);

+

+        registryDirectory.notify(serviceUrls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+        List invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+    }

+

+    @Test

+    public void testNotified_WithDuplicateUrls() {

+        List<URL> serviceUrls = new ArrayList<URL>();

+        // ignore error log

+        serviceUrls.add(SERVICEURL);

+        serviceUrls.add(SERVICEURL);

+

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        registryDirectory.notify(serviceUrls);

+        List invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+    }

+

+    // forbid

+    private void testforbid(RegistryDirectory registryDirectory) {

+        invocation = new RpcInvocation();

+        List<URL> serviceUrls = new ArrayList<URL>();

+        registryDirectory.notify(serviceUrls);

+        Assert.assertEquals("invokers size=0 ,then the registry directory is not available", false,

+                            registryDirectory.isAvailable());

+        try {

+            registryDirectory.list(invocation);

+            fail("forbid must throw RpcException");

+        } catch (RpcException e) {

+            Assert.assertEquals(RpcException.FORBIDDEN_EXCEPTION, e.getCode());

+        }

+    }

+

+    //测试调用和registry url的path无关

+    @Test

+    public void test_NotifiedDubbo1() {

+        URL             errorPathUrl            = URL.valueOf("notsupport:/" + "xxx"+"?interface="+service);

+        RegistryDirectory registryDirectory = getRegistryDirectory(errorPathUrl);

+        List<URL> serviceUrls = new ArrayList<URL>();

+        URL Dubbo1URL = URL.valueOf("dubbo://127.0.0.1:9098?lazy=true");

+        serviceUrls.add(Dubbo1URL.addParameter("methods", "getXXX"));

+        registryDirectory.notify(serviceUrls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+

+        invocation = new RpcInvocation();

+

+        List<Invoker<DemoService>> invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+

+        invocation.setMethodName("getXXX");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+        Assert.assertEquals(DemoService.class.getName(), invokers.get(0).getUrl().getPath());

+    }

+

+    // notify one invoker

+    private void test_Notified_only_routes(RegistryDirectory registryDirectory) {

+        List<URL> serviceUrls = new ArrayList<URL>();

+        serviceUrls.add(URL.valueOf("route://127.0.0.1/?router=clean"));

+        registryDirectory.notify(serviceUrls);

+    }

+    // notify one invoker

+    private void test_Notified1invokers(RegistryDirectory registryDirectory) {

+

+        List<URL> serviceUrls = new ArrayList<URL>();

+        serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));// .addParameter("refer.autodestroy", "true")

+        registryDirectory.notify(serviceUrls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+

+        invocation = new RpcInvocation();

+

+        List invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+

+        invocation.setMethodName("getXXX");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+

+        invocation.setMethodName("getXXX1");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+

+        invocation.setMethodName("getXXX2");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+    }

+

+    // 两个invoker===================================

+    private void test_Notified2invokers(RegistryDirectory registryDirectory) {

+        List<URL> serviceUrls = new ArrayList<URL>();

+        serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));

+        serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));

+        serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));

+

+        registryDirectory.notify(serviceUrls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+

+        invocation = new RpcInvocation();

+

+        List invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+

+        invocation.setMethodName("getXXX");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+

+        invocation.setMethodName("getXXX1");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+

+        invocation.setMethodName("getXXX2");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+    }

+

+    // 通知成3个invoker===================================

+    private void test_Notified3invokers(RegistryDirectory registryDirectory) {

+        List<URL> serviceUrls = new ArrayList<URL>();

+        serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));

+        serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));

+        serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1,getXXX2,getXXX3"));

+

+        registryDirectory.notify(serviceUrls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+

+        invocation = new RpcInvocation();

+

+        List invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(3, invokers.size());

+

+        invocation.setMethodName("getXXX");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(3, invokers.size());

+

+        invocation.setMethodName("getXXX1");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(3, invokers.size());

+

+        invocation.setMethodName("getXXX2");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+

+        invocation.setMethodName("getXXX3");

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+    }

+

+    @Test

+    public void testParametersMerge() {

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        URL regurl = noMeaningUrl.addParameter("test", "reg").addParameterAndEncoded(Constants.REFER_KEY,

+                                                                                     "key=query&"

+                                                                                             + Constants.LOADBALANCE_KEY

+                                                                                             + "="

+                                                                                             + LeastActiveLoadBalance.NAME);

+        RegistryDirectory<RegistryDirectoryTest> registryDirectory2 = new RegistryDirectory(

+                                                                                            RegistryDirectoryTest.class,

+                                                                                            regurl);

+        registryDirectory2.setProtocol(protocol);

+

+        List<URL> serviceUrls = new ArrayList<URL>();

+        // 检验注册中心的参数需要被清除

+        {

+            serviceUrls.clear();

+            serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));

+            registryDirectory.notify(serviceUrls);

+

+            invocation = new RpcInvocation();

+            List invokers = registryDirectory.list(invocation);

+

+            Invoker invoker = (Invoker) invokers.get(0);

+            URL url = invoker.getUrl();

+            Assert.assertEquals(null, url.getParameter("key"));

+        }

+        // 检验服务提供方的参数需要merge

+        {

+            serviceUrls.clear();

+            serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX2").addParameter("key", "provider"));

+

+            registryDirectory.notify(serviceUrls);

+            invocation = new RpcInvocation();

+            List invokers = registryDirectory.list(invocation);

+

+            Invoker invoker = (Invoker) invokers.get(0);

+            URL url = invoker.getUrl();

+            Assert.assertEquals("provider", url.getParameter("key"));

+        }

+        // 检验服务query的参数需要与providermerge 。

+        {

+            serviceUrls.clear();

+            serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX3").addParameter("key", "provider"));

+

+            registryDirectory2.notify(serviceUrls);

+            invocation = new RpcInvocation();

+            List invokers = registryDirectory2.list(invocation);

+

+            Invoker invoker = (Invoker) invokers.get(0);

+            URL url = invoker.getUrl();

+            Assert.assertEquals("query", url.getParameter("key"));

+        }

+

+        {

+            serviceUrls.clear();

+            serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));

+            registryDirectory.notify(serviceUrls);

+

+            invocation = new RpcInvocation();

+            List invokers = registryDirectory.list(invocation);

+

+            Invoker invoker = (Invoker) invokers.get(0);

+            URL url = invoker.getUrl();

+            Assert.assertEquals(false, url.getParameter(Constants.CHECK_KEY, false));

+        }

+        {

+            serviceUrls.clear();

+            serviceUrls.add(SERVICEURL.addParameter(Constants.LOADBALANCE_KEY, RoundRobinLoadBalance.NAME));

+            registryDirectory2.notify(serviceUrls);

+

+            invocation = new RpcInvocation();

+            invocation.setMethodName("get");

+            List invokers = registryDirectory2.list(invocation);

+

+            Invoker invoker = (Invoker) invokers.get(0);

+            URL url = invoker.getUrl();

+            Assert.assertEquals(LeastActiveLoadBalance.NAME, url.getMethodParameter("get", Constants.LOADBALANCE_KEY));

+        }

+        //test geturl

+        {

+        	Assert.assertEquals(null, registryDirectory2.getUrl().getParameter("mock"));

+            serviceUrls.clear();

+            serviceUrls.add(SERVICEURL.addParameter(Constants.MOCK_KEY, "true"));

+            registryDirectory2.notify(serviceUrls);

+

+            Assert.assertEquals("true", registryDirectory2.getUrl().getParameter("mock"));

+        }

+    }

+

+    /**

+     * When destroying, RegistryDirectory should: 1. be disconnected from Registry 2. destroy all invokers

+     */

+    @Test

+    public void testDestroy() {

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+

+        List<URL> serviceUrls = new ArrayList<URL>();

+        serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));

+        serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));

+        serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1,getXXX2,getXXX3"));

+

+        registryDirectory.notify(serviceUrls);

+        List<Invoker> invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+        Assert.assertEquals(true, invokers.get(0).isAvailable());

+

+        registryDirectory.destroy();

+        Assert.assertEquals(false, registryDirectory.isAvailable());

+        Assert.assertEquals(false, invokers.get(0).isAvailable());

+        registryDirectory.destroy();

+

+        Map<String, List<Invoker<RegistryDirectoryTest>>> methodInvokerMap = registryDirectory.getMethodInvokerMap();

+        Map<String, Invoker<RegistryDirectoryTest>> urlInvokerMap = registryDirectory.getUrlInvokerMap();

+

+        Assert.assertTrue(methodInvokerMap == null);

+        Assert.assertEquals(0, urlInvokerMap.size());

+        // List<U> urls = mockRegistry.getSubscribedUrls();

+

+        RpcInvocation inv = new RpcInvocation();

+        try {

+            registryDirectory.list(inv);

+            fail();

+        } catch (RpcException e) {

+            Assert.assertTrue(e.getMessage().contains("already destroyed"));

+        }

+    }

+

+    @Test

+    public void testDestroy_WithDestroyRegistry() {

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        CountDownLatch latch = new CountDownLatch(1);

+        registryDirectory.setRegistry(new MockRegistry(latch));

+        registryDirectory.destroy();

+        Assert.assertEquals(0, latch.getCount());

+    }

+

+    @Test

+    public void testDestroy_WithDestroyRegistry_WithError() {

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        registryDirectory.setRegistry(new MockRegistry(true));

+        registryDirectory.destroy();

+    }

+

+    @Test

+    public void testDubbo1UrlWithGenericInvocation() {

+

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+

+        List<URL> serviceUrls = new ArrayList<URL>();

+        URL serviceURL = SERVICEURL_DUBBO_NOPATH.addParameter("methods", "getXXX1,getXXX2,getXXX3");

+        serviceUrls.add(serviceURL);

+

+        registryDirectory.notify(serviceUrls);

+

+        // Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;

+        invocation = new RpcInvocation(Constants.$INVOKE, new Class[] { String.class, String[].class, Object[].class },

+                                       new Object[] { "getXXX1", "", new Object[] {} });

+

+        List<Invoker> invokers = registryDirectory.list(invocation);

+

+        Assert.assertEquals(1, invokers.size());

+        Assert.assertEquals(serviceURL.setPath(service).addParameter("check", "false"), invokers.get(0).getUrl());

+

+    }

+

+    enum Param {

+        MORGAN,

+    };

+

+    /**

+     * When the first arg of a method is String or Enum, Registry server can do parameter-value-based routing.

+     */

+    @Test

+    public void testParmeterRoute() {

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        List<URL> serviceUrls = new ArrayList<URL>();

+        serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1.napoli"));

+        serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1.MORGAN,getXXX2"));

+        serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1.morgan,getXXX2,getXXX3"));

+

+        registryDirectory.notify(serviceUrls);

+

+        invocation = new RpcInvocation(

+                                       Constants.$INVOKE,

+                                       new Class[] { String.class, String[].class, Object[].class },

+                                       new Object[] { "getXXX1", new String[] { "Enum" }, new Object[] { Param.MORGAN } });

+

+        List invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(1, invokers.size());

+    }

+

+    /**

+     * Empty notify cause forbidden, non-empty notify cancels forbidden state

+     */

+    @Test

+    public void testEmptyNotifyCauseForbidden() {

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        List invokers = null;

+

+        List<URL> serviceUrls = new ArrayList<URL>();

+        registryDirectory.notify(serviceUrls);

+

+        RpcInvocation inv = new RpcInvocation();

+        try {

+            invokers = registryDirectory.list(inv);

+        } catch (RpcException e) {

+            Assert.assertEquals(RpcException.FORBIDDEN_EXCEPTION, e.getCode());

+            Assert.assertEquals(false, registryDirectory.isAvailable());

+        }

+

+        serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));

+        serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));

+        serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1,getXXX2,getXXX3"));

+

+        registryDirectory.notify(serviceUrls);

+        inv.setMethodName("getXXX2");

+        invokers = registryDirectory.list(inv);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+        Assert.assertEquals(2, invokers.size());

+    }

+

+    private static boolean isScriptUnsupported = new ScriptEngineManager().getEngineByName("javascript") == null;

+

+    /**

+     * 1. notify twice, the second time notified router rules should completely replace the former one. 2. notify with

+     * no router url, do nothing to current routers 3. notify with only one router url, with router=clean, clear all

+     * current routers

+     */

+    @Test

+    public void testNotifyRouterUrls() {

+        if (isScriptUnsupported) return;

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        URL routerurl = URL.valueOf(Constants.ROUTE_PROTOCOL + "://127.0.0.1:9096/");

+        URL routerurl2 = URL.valueOf(Constants.ROUTE_PROTOCOL + "://127.0.0.1:9097/");

+

+        List<URL> serviceUrls = new ArrayList<URL>();

+        // without ROUTER_KEY, the first router should not be created.

+        serviceUrls.add(routerurl.addParameter(Constants.TYPE_KEY, "javascript").addParameter(Constants.ROUTER_KEY,

+                                                                                                 "notsupported").addParameter(Constants.RULE_KEY,

+                                                                                                                              "function test1(){}"));

+        serviceUrls.add(routerurl2.addParameter(Constants.TYPE_KEY, "javascript").addParameter(Constants.ROUTER_KEY,

+                                                                                                  ScriptRouterFactory.NAME).addParameter(Constants.RULE_KEY,

+                                                                                                                                         "function test1(){}"));

+

+        registryDirectory.notify(serviceUrls);

+        List<Router> routers = registryDirectory.getRouters();

+        //default invocation selector

+        Assert.assertEquals(1+1, routers.size());

+        Assert.assertEquals(ScriptRouter.class, routers.get(0).getClass());

+

+        registryDirectory.notify(new ArrayList<URL>());

+        routers = registryDirectory.getRouters();

+        Assert.assertEquals(1 + 1, routers.size());

+        Assert.assertEquals(ScriptRouter.class, routers.get(0).getClass());

+

+        serviceUrls.clear();

+        serviceUrls.add(routerurl.addParameter(Constants.ROUTER_KEY, Constants.ROUTER_TYPE_CLEAR));

+        registryDirectory.notify(serviceUrls);

+        routers = registryDirectory.getRouters();

+        Assert.assertEquals(0 + 1, routers.size());

+    }

+    

+    /**

+     * 测试override规则是否优先

+     * 场景:先推送override,后推送invoker

+     */

+    @Test

+    public void testNotifyoverrideUrls_beforeInvoker(){

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        List<URL> overrideUrls = new ArrayList<URL>();

+        overrideUrls.add(URL.valueOf("override://0.0.0.0?timeout=1&connections=5"));

+        registryDirectory.notify(overrideUrls);

+        //注册中心初始只推送override,dirctory状态应该是false,因为没有invoker存在。

+        Assert.assertEquals(false, registryDirectory.isAvailable());

+        

+        //在推送两个provider,directory状态恢复为true

+        List<URL> serviceUrls = new ArrayList<URL>();

+        serviceUrls.add(SERVICEURL.addParameter("timeout", "1000"));

+        serviceUrls.add(SERVICEURL2.addParameter("timeout", "1000").addParameter("connections", "10"));

+

+        registryDirectory.notify(serviceUrls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+        

+        //开始验证参数值

+

+        invocation = new RpcInvocation();

+

+        List<Invoker<?>> invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+        

+        Assert.assertEquals("override rute must be first priority", "1", invokers.get(0).getUrl().getParameter("timeout"));

+        Assert.assertEquals("override rute must be first priority", "5", invokers.get(0).getUrl().getParameter("connections"));

+    }

+    

+    /**

+     * 测试override规则是否优先

+     * 场景:先推送override,后推送invoker

+     */

+    @Test

+    public void testNotifyoverrideUrls_afterInvoker(){

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        

+        //在推送两个provider,directory状态恢复为true

+        List<URL> serviceUrls = new ArrayList<URL>();

+        serviceUrls.add(SERVICEURL.addParameter("timeout", "1000"));

+        serviceUrls.add(SERVICEURL2.addParameter("timeout", "1000").addParameter("connections", "10"));

+

+        registryDirectory.notify(serviceUrls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+        

+        List<URL> overrideUrls = new ArrayList<URL>();

+        overrideUrls.add(URL.valueOf("override://0.0.0.0?timeout=1&connections=5"));

+        registryDirectory.notify(overrideUrls);

+        

+        //开始验证参数值

+

+        invocation = new RpcInvocation();

+

+        List<Invoker<?>> invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+        

+        Assert.assertEquals("override rute must be first priority", "1", invokers.get(0).getUrl().getParameter("timeout"));

+        Assert.assertEquals("override rute must be first priority", "5", invokers.get(0).getUrl().getParameter("connections"));

+    }

+    

+    /**

+     * 测试override规则是否优先

+     * 场景:与invoker 一起推override规则

+     */

+    @Test

+    public void testNotifyoverrideUrls_withInvoker(){

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        

+        List<URL> durls = new ArrayList<URL>();

+        durls.add(SERVICEURL.addParameter("timeout", "1000"));

+        durls.add(SERVICEURL2.addParameter("timeout", "1000").addParameter("connections", "10"));

+        durls.add(URL.valueOf("override://0.0.0.0?timeout=1&connections=5"));

+

+        registryDirectory.notify(durls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+        

+        //开始验证参数值

+

+        invocation = new RpcInvocation();

+

+        List<Invoker<?>> invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+        

+        Assert.assertEquals("override rute must be first priority", "1", invokers.get(0).getUrl().getParameter("timeout"));

+        Assert.assertEquals("override rute must be first priority", "5", invokers.get(0).getUrl().getParameter("connections"));

+    }

+    

+    /**

+     * 测试override规则是否优先

+     * 场景:推送的规则与provider的参数是一样的

+     * 期望:不需要重新引用

+     */

+    @Test

+    public void testNotifyoverrideUrls_Nouse(){

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        invocation = new RpcInvocation();

+        

+        List<URL> durls = new ArrayList<URL>();

+        durls.add(SERVICEURL.addParameter("timeout", "1"));//一个一样,一个不一样

+        durls.add(SERVICEURL2.addParameter("timeout", "1").addParameter("connections", "5"));

+        registryDirectory.notify(durls);

+        List<Invoker<?>> invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+        Invoker<?> a1Invoker = invokers.get(0);

+        Invoker<?> b1Invoker = invokers.get(1);

+        

+        durls = new ArrayList<URL>();

+        durls.add(URL.valueOf("override://0.0.0.0?timeout=1&connections=5"));

+        registryDirectory.notify(durls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+        

+        invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+        

+        Invoker<?> a2Invoker = invokers.get(0);

+        Invoker<?> b2Invoker = invokers.get(1);

+        //参数不一样,必须重新引用

+        Assert.assertFalse("object not same",a1Invoker == a2Invoker);

+        

+        //参数一样,不能重新引用

+        Assert.assertTrue("object same",b1Invoker == b2Invoker);

+    }

+    

+    /**

+     * 测试针对某个provider的Override规则

+     */

+    @Test

+    public void testNofityOverrideUrls_Provider(){

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        invocation = new RpcInvocation();

+        

+        List<URL> durls = new ArrayList<URL>();

+        durls.add(SERVICEURL.setHost("10.20.30.140").addParameter("timeout", "1"));//一个一样,一个不一样

+        durls.add(SERVICEURL2.setHost("10.20.30.141").addParameter("timeout", "2"));

+        registryDirectory.notify(durls);

+        

+        durls = new ArrayList<URL>();

+        durls.add(URL.valueOf("override://0.0.0.0?timeout=3"));

+        durls.add(URL.valueOf("override://10.20.30.141:9092?timeout=4"));

+        registryDirectory.notify(durls);

+        

+        List<Invoker<?>> invokers = registryDirectory.list(invocation);

+        Invoker<?> aInvoker = invokers.get(0);

+        Invoker<?> bInvoker = invokers.get(1);

+        Assert.assertEquals("3",aInvoker.getUrl().getParameter("timeout"));

+        Assert.assertEquals("4",bInvoker.getUrl().getParameter("timeout"));

+    }

+    

+    /**

+     * 测试清除override规则,同时下发清除规则和其他override规则

+     * 测试是否能够恢复到推送时的providerUrl

+     */

+    @Test

+    public void testNofityOverrideUrls_Clean1(){

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        invocation = new RpcInvocation();

+        

+        List<URL> durls = new ArrayList<URL>();

+        durls.add(SERVICEURL.setHost("10.20.30.140").addParameter("timeout", "1"));

+        registryDirectory.notify(durls);

+        

+        durls = new ArrayList<URL>();

+        durls.add(URL.valueOf("override://0.0.0.0?timeout=1000"));

+        registryDirectory.notify(durls);

+        

+        durls = new ArrayList<URL>();

+        durls.add(URL.valueOf("override://0.0.0.0?timeout=3"));

+        durls.add(URL.valueOf("override://0.0.0.0"));

+        registryDirectory.notify(durls);

+        

+        List<Invoker<?>> invokers = registryDirectory.list(invocation);

+        Invoker<?> aInvoker = invokers.get(0);

+        //需要恢复到最初的providerUrl

+        Assert.assertEquals("1",aInvoker.getUrl().getParameter("timeout"));

+    }

+    

+    /**

+     * 测试清除override规则,只下发override清除规则

+     * 测试是否能够恢复到推送时的providerUrl

+     */

+    @Test

+    public void testNofityOverrideUrls_CleanOnly(){

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        invocation = new RpcInvocation();

+        

+        List<URL> durls = new ArrayList<URL>();

+        durls.add(SERVICEURL.setHost("10.20.30.140").addParameter("timeout", "1"));

+        registryDirectory.notify(durls);

+        Assert.assertEquals(null,registryDirectory.getUrl().getParameter("mock"));

+        

+        //override

+        durls = new ArrayList<URL>();

+        durls.add(URL.valueOf("override://0.0.0.0?timeout=1000&mock=fail"));

+        registryDirectory.notify(durls);

+        List<Invoker<?>> invokers = registryDirectory.list(invocation);

+        Invoker<?> aInvoker = invokers.get(0);

+        Assert.assertEquals("1000",aInvoker.getUrl().getParameter("timeout"));

+        Assert.assertEquals("fail",registryDirectory.getUrl().getParameter("mock"));

+        

+        //override clean

+        durls = new ArrayList<URL>();

+        durls.add(URL.valueOf("override://100.16.128.100/dubbo.test.api.HelloService?anyhost=true"));

+        registryDirectory.notify(durls);

+        invokers = registryDirectory.list(invocation);

+        aInvoker = invokers.get(0);

+        //需要恢复到最初的providerUrl

+        Assert.assertEquals("1",aInvoker.getUrl().getParameter("timeout"));

+        

+        Assert.assertEquals(null,registryDirectory.getUrl().getParameter("mock"));

+    }

+    

+    /**

+     * 测试同时推送清除override和针对某个provider的override

+     * 看override是否能够生效

+     */

+    @Test

+    public void testNofityOverrideUrls_CleanNOverride(){

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        invocation = new RpcInvocation();

+        

+        List<URL> durls = new ArrayList<URL>();

+        durls.add(SERVICEURL.setHost("10.20.30.140").addParameter("timeout", "1"));

+        registryDirectory.notify(durls);

+        

+        durls = new ArrayList<URL>();

+        durls.add(URL.valueOf("override://0.0.0.0?timeout=3"));

+        durls.add(URL.valueOf("override://0.0.0.0"));

+        durls.add(URL.valueOf("override://10.20.30.140:9091?timeout=4"));

+        registryDirectory.notify(durls);

+        

+        List<Invoker<?>> invokers = registryDirectory.list(invocation);

+        Invoker<?> aInvoker = invokers.get(0);

+        Assert.assertEquals("4",aInvoker.getUrl().getParameter("timeout"));

+    }

+

+    @Test

+    public void testNotifyRouterUrls_Clean() {

+        if (isScriptUnsupported) return;

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        URL routerurl = URL.valueOf(Constants.ROUTE_PROTOCOL + "://127.0.0.1:9096/").addParameter(Constants.ROUTER_KEY,

+                                                                                                     "javascript").addParameter(Constants.RULE_KEY,

+                                                                                                                                "function test1(){}").addParameter(Constants.ROUTER_KEY,

+                                                                                                                                                                   "script"); // FIX

+                                                                                                                                                                              // BAD

+

+        List<URL> serviceUrls = new ArrayList<URL>();

+        // without ROUTER_KEY, the first router should not be created.

+        serviceUrls.add(routerurl);

+        registryDirectory.notify(serviceUrls);

+        List routers = registryDirectory.getRouters();

+        Assert.assertEquals(1 + 1, routers.size());

+

+        serviceUrls.clear();

+        serviceUrls.add(routerurl.addParameter(Constants.ROUTER_KEY, Constants.ROUTER_TYPE_CLEAR));

+        registryDirectory.notify(serviceUrls);

+        routers = registryDirectory.getRouters();

+        Assert.assertEquals(0 + 1, routers.size());

+    }

+

+    // mock protocol

+    /**

+     * 测试mock provider下发

+     */

+    @Test

+    public void testNotify_MockProviderOnly() {

+    	RegistryDirectory registryDirectory = getRegistryDirectory();

+    	

+    	List<URL> serviceUrls = new ArrayList<URL>();

+        serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));

+        serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));

+        serviceUrls.add(SERVICEURL.setProtocol(Constants.MOCK_PROTOCOL));

+

+        registryDirectory.notify(serviceUrls);

+        Assert.assertEquals(true, registryDirectory.isAvailable());

+        invocation = new RpcInvocation();

+

+        List invokers = registryDirectory.list(invocation);

+        Assert.assertEquals(2, invokers.size());

+    	

+        RpcInvocation mockinvocation = new RpcInvocation();

+        mockinvocation.setAttachment(Constants.INVOCATION_NEED_MOCK, "true");

+        invokers = registryDirectory.list(mockinvocation);

+        Assert.assertEquals(1, invokers.size());

+    }

+    

+    private static interface DemoService {

+    }

+

+    private static class MockRegistry implements Registry {

+

+        CountDownLatch latch;

+        boolean        destroyWithError;

+

+        public MockRegistry(CountDownLatch latch){

+            this.latch = latch;

+        }

+

+        public MockRegistry(boolean destroyWithError){

+            this.destroyWithError = destroyWithError;

+        }

+

+        public void register(URL url, NotifyListener listener) {

+

+        }

+

+        public void unregister(URL url, NotifyListener listener) {

+

+        }

+

+        public void subscribe(URL url, NotifyListener listener) {

+

+        }

+

+        public void unsubscribe(URL url, NotifyListener listener) {

+            if (latch != null )latch.countDown();

+        }

+

+        public List<URL> lookup(URL url) {

+            return null;

+        }

+

+        public URL getUrl() {

+            return null;

+        }

+

+        public boolean isAvailable() {

+            return true;

+        }

+

+        public void destroy() {

+            if (destroyWithError) {

+                throw new RpcException("test exception ignore.");

+            }

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.java b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.java
new file mode 100644
index 0000000..5312f6d
--- /dev/null
+++ b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.java
@@ -0,0 +1,193 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.dubbo;

+

+import static org.junit.Assert.assertEquals;

+

+import java.lang.reflect.Field;

+import java.util.ArrayList;

+import java.util.List;

+

+import org.junit.Assert;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.RegistryFactory;

+import com.alibaba.dubbo.registry.support.AbstractRegistry;

+import com.alibaba.dubbo.registry.support.RegistryProtocol;

+import com.alibaba.dubbo.registry.support.SimpleRegistryExporter;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.cluster.support.FailfastCluster;

+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * RegistryProtocolTest

+ * 

+ * @author tony.chenl

+ */

+public class RegistryProtocolTest {

+    

+    final private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+

+    static {

+        SimpleRegistryExporter.exportIfAbsent(9090);

+    }

+

+    final String service     = "com.alibaba.dubbo.registry.protocol.DemoService:1.0.0";

+    final String serviceUrl  = "dubbo://127.0.0.1:9453/" + service + "?notify=true&methods=test1,test2";

+    final URL    registryUrl = URL.valueOf("registry://127.0.0.1:9090/");

+

+    @Test

+    public void testDefaultPort() {

+        RegistryProtocol registryProtocol = new RegistryProtocol();

+        assertEquals(9090, registryProtocol.getDefaultPort());

+    }

+

+    @Test(expected = IllegalArgumentException.class)

+    public void testExportUrlNull() {

+        RegistryProtocol registryProtocol = new RegistryProtocol();

+        registryProtocol.setCluster(new FailfastCluster());

+

+        Protocol dubboProtocol = DubboProtocol.getDubboProtocol();

+        registryProtocol.setProtocol(dubboProtocol);

+        Invoker<DemoService> invoker = new DubboInvoker<DemoService>(DemoService.class,

+                registryUrl, new ExchangeClient[] { new MockedClient("10.20.20.20", 2222, true) });

+        registryProtocol.export(invoker);

+    }

+

+    @Test

+    public void testExport() {

+        RegistryProtocol registryProtocol = new RegistryProtocol();

+        registryProtocol.setCluster(new FailfastCluster());

+        registryProtocol.setRegistryFactory(ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension());

+

+        Protocol dubboProtocol = DubboProtocol.getDubboProtocol();

+        registryProtocol.setProtocol(dubboProtocol);

+        URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl);

+        DubboInvoker<DemoService> invoker = new DubboInvoker<DemoService>(DemoService.class,

+                newRegistryUrl, new ExchangeClient[] { new MockedClient("10.20.20.20", 2222, true) });

+        Exporter<DemoService> exporter = registryProtocol.export(invoker);

+        Exporter<DemoService> exporter2 = registryProtocol.export(invoker);

+        //同一个invoker,多次export的exporter不同

+        Assert.assertNotSame(exporter, exporter2);

+        exporter.unexport();

+        exporter2.unexport();

+        

+    }

+    

+    @Test

+    public void testNotifyOverride() throws Exception{

+        URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl);

+        Invoker<RegistryProtocolTest> invoker = new MockInvoker<RegistryProtocolTest>(RegistryProtocolTest.class, newRegistryUrl);

+        Exporter<?> exporter = protocol.export(invoker);

+        RegistryProtocol rprotocol = RegistryProtocol.getRegistryProtocol();

+        NotifyListener listener = getListener(rprotocol);

+        List<URL> urls = new ArrayList<URL>();

+        urls.add(URL.valueOf("override://0.0.0.0/?timeout=1000"));

+        urls.add(URL.valueOf("override://0.0.0.0/"+ service + "?timeout=100"));

+        urls.add(URL.valueOf("override://0.0.0.0/"+ service + "?x=y"));

+        listener.notify(urls);

+        

+        assertEquals(true, exporter.getInvoker().isAvailable());

+        assertEquals("100", exporter.getInvoker().getUrl().getParameter("timeout"));

+        assertEquals("y", exporter.getInvoker().getUrl().getParameter("x"));

+        

+        exporter.unexport();

+        assertEquals(false, exporter.getInvoker().isAvailable());

+        destroyRegistryProtocol();

+        

+    }

+    

+    

+    /**

+     * 服务名称不匹配,不能override invoker

+     * 服务名称匹配,服务版本号不匹配

+     */

+    @Test

+    public void testNotifyOverride_notmatch() throws Exception{

+        URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl);

+        Invoker<RegistryProtocolTest> invoker = new MockInvoker<RegistryProtocolTest>(RegistryProtocolTest.class, newRegistryUrl);

+        Exporter<?> exporter = protocol.export(invoker);

+        RegistryProtocol rprotocol = RegistryProtocol.getRegistryProtocol();

+        NotifyListener listener = getListener(rprotocol);

+        List<URL> urls = new ArrayList<URL>();

+        urls.add(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.registry.protocol.HackService?timeout=100"));

+        listener.notify(urls);

+        assertEquals(true, exporter.getInvoker().isAvailable());

+        assertEquals(null, exporter.getInvoker().getUrl().getParameter("timeout"));

+        exporter.unexport();

+        destroyRegistryProtocol();

+    }

+    

+    /**

+     *测试destory registry ,exporter是否能够正常被destroy掉 

+     */

+    @Test

+    public void testDestoryRegistry(){

+        URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl);

+        Invoker<RegistryProtocolTest> invoker = new MockInvoker<RegistryProtocolTest>(RegistryProtocolTest.class, newRegistryUrl);

+        Exporter<?> exporter = protocol.export(invoker);

+        destroyRegistryProtocol();

+        assertEquals(false, exporter.getInvoker().isAvailable());

+        

+    }

+    

+    private void destroyRegistryProtocol(){

+        Protocol registry = RegistryProtocol.getRegistryProtocol();

+        registry.destroy();

+    }

+

+    private NotifyListener getListener(RegistryProtocol protocol) throws Exception {

+        Field field = RegistryProtocol.class.getDeclaredField("listener");

+        field.setAccessible(true);

+        NotifyListener listener = (NotifyListener) field.get(protocol);

+        return listener;

+    }

+    

+    static class MockInvoker<T> extends AbstractInvoker<T>{

+        public MockInvoker(Class<T> type, URL url) {

+            super(type, url);

+        }

+

+        @Override

+        protected Result doInvoke(Invocation invocation) throws Throwable {

+            //do nothing

+            return null;

+        }

+    }

+    

+    static class MockRegistry extends AbstractRegistry{

+

+        public MockRegistry(URL url) {

+            super(url);

+        }

+

+        public boolean isAvailable() {

+            return true;

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryStatusCheckerTest.java b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryStatusCheckerTest.java
new file mode 100644
index 0000000..fc8750a
--- /dev/null
+++ b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryStatusCheckerTest.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.dubbo;

+

+import static org.junit.Assert.assertEquals;

+

+import org.junit.Assert;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.registry.RegistryFactory;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+import com.alibaba.dubbo.registry.support.RegistryStatusChecker;

+import com.alibaba.dubbo.registry.support.SimpleRegistryExporter;

+

+/**

+ * StatusTest

+ * 

+ * @author tony.chenl

+ */

+public class RegistryStatusCheckerTest {

+

+    static {

+        SimpleRegistryExporter.exportIfAbsent(9090);

+        SimpleRegistryExporter.exportIfAbsent(9091);

+    }

+    URL registryUrl = URL.valueOf("dubbo://cat:cat@127.0.0.1:9090/");

+    URL registryUrl2 = URL.valueOf("dubbo://cat:cat@127.0.0.1:9091");

+

+    @Before

+    public void setUp() {

+        AbstractRegistryFactory.destroyAll();

+    }

+

+    @Test

+    public void testCheckUnknown() {

+        assertEquals(Status.Level.UNKNOWN, new RegistryStatusChecker().check().getLevel());

+    }

+

+    @Test

+    public void testCheckOK() {

+        ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(registryUrl);

+        ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(registryUrl2);

+        assertEquals(Status.Level.OK, new RegistryStatusChecker().check().getLevel());

+        String message = new RegistryStatusChecker().check().getMessage();

+        Assert.assertTrue(message.contains(registryUrl.getAddress() + "(connected)"));

+        Assert.assertTrue(message.contains(registryUrl2.getAddress() + "(connected)"));

+    }

+}
\ No newline at end of file
diff --git a/dubbo-registry-default/src/test/resources/log4j.xml b/dubbo-registry-default/src/test/resources/log4j.xml
new file mode 100644
index 0000000..998d18c
--- /dev/null
+++ b/dubbo-registry-default/src/test/resources/log4j.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

+	<!-- ===================================================================== -->

+	<!-- 以下是appender的定义 -->

+	<!-- ===================================================================== -->

+	<appender name="dubbo" class="com.alibaba.dubbo.common.utils.DubboAppender">

+		<param name="encoding" value="GBK" />

+		<layout class="org.apache.log4j.PatternLayout">

+			<param name="ConversionPattern" value="%d %p [%c:%M] - %m%n" />

+		</layout>

+		<!-- <filter class="org.apache.log4j.varia.LevelRangeFilter">

+			<param name="LevelMin" value="DEBUG" />

+			<param name="LevelMax" value="DEBUG" />

+		</filter> -->

+	</appender>

+	<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

+		<layout class="org.apache.log4j.PatternLayout">

+			<param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %5p %c{2}: %m%n" />

+		</layout>

+	</appender>

+	<root>

+		<level value="INFO" />

+		<appender-ref ref="dubbo" />

+		<appender-ref ref="CONSOLE" />

+	</root>

+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-registry-multicast/pom.xml b/dubbo-registry-multicast/pom.xml
new file mode 100644
index 0000000..048b0a0
--- /dev/null
+++ b/dubbo-registry-multicast/pom.xml
@@ -0,0 +1,38 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-registry-multicast</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Multicast Registry Module</name>
+	<description>The multicast registry module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-registry</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
new file mode 100644
index 0000000..234968b
--- /dev/null
+++ b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
@@ -0,0 +1,296 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.multicast;

+

+import java.io.IOException;

+import java.net.DatagramPacket;

+import java.net.InetAddress;

+import java.net.InetSocketAddress;

+import java.net.MulticastSocket;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.common.utils.UrlUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.support.FailbackRegistry;

+

+/**

+ * MulticastRegistry

+ * 

+ * @author william.liangf

+ */

+public class MulticastRegistry extends FailbackRegistry {

+

+    // 日志输出

+    private static final Logger logger = LoggerFactory.getLogger(MulticastRegistry.class);

+

+    private static final int DEFAULT_MULTICAST_PORT = 1234;

+

+    private final InetAddress mutilcastAddress;

+    

+    private final MulticastSocket mutilcastSocket;

+

+    private final ConcurrentMap<String, Set<String>> notified = new ConcurrentHashMap<String, Set<String>>();

+

+    public MulticastRegistry(URL url) {

+        super(url);

+        if (! isMulticastAddress(url.getHost())) {

+            throw new IllegalArgumentException("Invalid multicast address " + url.getHost() + ", scope: 224.0.0.0 - 239.255.255.255");

+        }

+        try {

+            mutilcastAddress = InetAddress.getByName(url.getHost());

+            mutilcastSocket = new MulticastSocket(url.getPort() == 0 ? DEFAULT_MULTICAST_PORT : url.getPort());

+            mutilcastSocket.setLoopbackMode(false);

+            mutilcastSocket.joinGroup(mutilcastAddress);

+            Thread thread = new Thread(new Runnable() {

+                public void run() {

+                    byte[] buf = new byte[2048];

+                    DatagramPacket recv = new DatagramPacket(buf, buf.length);

+                    while (! mutilcastSocket.isClosed()) {

+                        try {

+                            mutilcastSocket.receive(recv);

+                            String msg = new String(recv.getData()).trim();

+                            int i = msg.indexOf('\n');

+                            if (i > 0) {

+                                msg = msg.substring(0, i).trim();

+                            }

+                            MulticastRegistry.this.receive(msg, (InetSocketAddress) recv.getSocketAddress());

+                            Arrays.fill(buf, (byte)0);

+                        } catch (Throwable e) {

+                            if (! mutilcastSocket.isClosed()) {

+                                logger.error(e.getMessage(), e);

+                            }

+                        }

+                    }

+                }

+            }, "DubboMulticastRegistryReceiver");

+            thread.setDaemon(true);

+            thread.start();

+        } catch (IOException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+    

+    private static boolean isMulticastAddress(String ip) {

+        int i = ip.indexOf('.');

+        if (i > 0) {

+            String prefix = ip.substring(0, i);

+            if (StringUtils.isInteger(prefix)) {

+                int p = Integer.parseInt(prefix);

+                return p >= 224 && p <= 239;

+            }

+        }

+        return false;

+    }

+

+    private void receive(String msg, InetSocketAddress remoteAddress) {

+        if (logger.isInfoEnabled()) {

+            logger.info("Receive multicast message: " + msg + " from " + remoteAddress);

+        }

+        if (msg.startsWith(Constants.REGISTER)) {

+            URL url = URL.valueOf(msg.substring(Constants.REGISTER.length()).trim());

+            registered(url);

+        } else if (msg.startsWith(Constants.UNREGISTER)) {

+            URL url = URL.valueOf(msg.substring(Constants.UNREGISTER.length()).trim());

+            unregistered(url);

+        } else if (msg.startsWith(Constants.SUBSCRIBE)) {

+            URL url = URL.valueOf(msg.substring(Constants.SUBSCRIBE.length()).trim());

+            List<URL> urls = lookup(url);

+            if (urls != null && urls.size() > 0) {

+                for (URL u : urls) {

+                    String host = remoteAddress != null && remoteAddress.getAddress() != null 

+                            ? remoteAddress.getAddress().getHostAddress() : url.getIp();

+                    if (url.getParameter("unicast", true) // 消费者的机器是否只有一个进程

+                            && ! NetUtils.getLocalHost().equals(host)) { // 同机器多进程不能用unicast单播信息,否则只会有一个进程收到信息

+                        unicast(Constants.REGISTER + " " + u.toFullString(), host);

+                    } else {

+                        broadcast(Constants.REGISTER + " " + u.toFullString());

+                    }

+                }

+            }

+        }/* else if (msg.startsWith(UNSUBSCRIBE)) {

+        }*/

+    }

+    

+    private void broadcast(String msg) {

+        if (logger.isInfoEnabled()) {

+            logger.info("Send broadcast message: " + msg + " to " + mutilcastAddress + ":" + mutilcastSocket.getLocalPort());

+        }

+        try {

+            byte[] data = (msg + "\n").getBytes();

+            DatagramPacket hi = new DatagramPacket(data, data.length, mutilcastAddress, mutilcastSocket.getLocalPort());

+            mutilcastSocket.send(hi);

+        } catch (Exception e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+    

+    private void unicast(String msg, String host) {

+        if (logger.isInfoEnabled()) {

+            logger.info("Send unicast message: " + msg + " to " + host + ":" + mutilcastSocket.getLocalPort());

+        }

+        try {

+            byte[] data = (msg + "\n").getBytes();

+            DatagramPacket hi = new DatagramPacket(data, data.length, InetAddress.getByName(host), mutilcastSocket.getLocalPort());

+            mutilcastSocket.send(hi);

+        } catch (Exception e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+    

+    protected void doRegister(URL url) {

+        broadcast(Constants.REGISTER + " " + url.toFullString());

+    }

+

+    protected void doUnregister(URL url) {

+        broadcast(Constants.UNREGISTER + " " + url.toFullString());

+    }

+

+    protected void doSubscribe(URL url, NotifyListener listener) {

+        if (! Constants.ANY_VALUE.equals(url.getServiceInterface())

+                && url.getParameter(Constants.REGISTER_KEY, true)) {

+            register(url, null);

+        }

+        broadcast(Constants.SUBSCRIBE + " " + url.toFullString());

+        synchronized (listener) {

+            try {

+                listener.wait(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));

+            } catch (InterruptedException e) {

+            }

+        }

+    }

+

+    protected void doUnsubscribe(URL url, NotifyListener listener) {

+        if (! Constants.ANY_VALUE.equals(url.getServiceInterface())

+                && url.getParameter(Constants.REGISTER_KEY, true)) {

+            unregister(url, null);

+        }

+        broadcast(Constants.UNSUBSCRIBE + " " + url.toFullString());

+    }

+

+    public boolean isAvailable() {

+        try {

+            return mutilcastSocket != null;

+        } catch (Throwable t) {

+            return false;

+        }

+    }

+

+    public void destroy() {

+        super.destroy();

+        try {

+            mutilcastSocket.leaveGroup(mutilcastAddress);

+            mutilcastSocket.close();

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+    }

+

+    protected void registered(URL url) {

+        for (Map.Entry<String, Set<NotifyListener>> entry : getSubscribed().entrySet()) {

+            String key = entry.getKey();

+            URL subscribe = URL.valueOf(key);

+            if (UrlUtils.isMatch(subscribe, url)) {

+                Set<String> urls = notified.get(key);

+                if (urls == null) {

+                    notified.putIfAbsent(key, new ConcurrentHashSet<String>());

+                    urls = notified.get(key);

+                }

+                urls.add(url.toFullString());

+                List<URL> list = toList(urls);

+                for (NotifyListener listener : entry.getValue()) {

+                    notify(subscribe, listener, list);

+                    synchronized (listener) {

+                        listener.notify();

+                    }

+                }

+            }

+        }

+    }

+

+    protected void unregistered(URL url) {

+        for (Map.Entry<String, Set<NotifyListener>> entry : getSubscribed().entrySet()) {

+            String key = entry.getKey();

+            URL subscribe = URL.valueOf(key);

+            if (UrlUtils.isMatch(subscribe, url)) {

+                Set<String> urls = notified.get(key);

+                if (urls != null) {

+                    urls.remove(url.toFullString());

+                }

+                List<URL> list = toList(urls);

+                for (NotifyListener listener : entry.getValue()) {

+                    notify(subscribe, listener, list);

+                }

+            }

+        }

+    }

+

+    protected void subscribed(URL url, NotifyListener listener) {

+        List<URL> urls = lookup(url);

+        notify(url, listener, urls);

+    }

+

+    private List<URL> toList(Set<String> urls) {

+        List<URL> list = new ArrayList<URL>();

+        if (urls != null && urls.size() > 0) {

+            for (String url : urls) {

+                list.add(URL.valueOf(url));

+            }

+        }

+        return list;

+    }

+

+    public void register(URL url, NotifyListener listener) {

+        super.register(url, listener);

+        registered(url);

+    }

+

+    public void unregister(URL url, NotifyListener listener) {

+        super.unregister(url, listener);

+        unregistered(url);

+    }

+

+    public void subscribe(URL url, NotifyListener listener) {

+        super.subscribe(url, listener);

+        subscribed(url, listener);

+    }

+

+    public void unsubscribe(URL url, NotifyListener listener) {

+        super.unsubscribe(url, listener);

+        notified.remove(url.toFullString());

+    }

+

+    public Map<String, Set<String>> getNotified() {

+        return notified;

+    }

+

+    public MulticastSocket getMutilcastSocket() {

+        return mutilcastSocket;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.java b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.java
new file mode 100644
index 0000000..138c952
--- /dev/null
+++ b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.multicast;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+

+/**

+ * MulticastRegistryLocator

+ * 

+ * @author william.liangf

+ */

+public class MulticastRegistryFactory extends AbstractRegistryFactory {

+

+    public Registry createRegistry(URL url) {

+        return new MulticastRegistry(url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..7eb7b3f
--- /dev/null
+++ b/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+multicast=com.alibaba.dubbo.registry.multicast.MulticastRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry-multicast/src/test/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryTest.java b/dubbo-registry-multicast/src/test/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryTest.java
new file mode 100644
index 0000000..ed1349f
--- /dev/null
+++ b/dubbo-registry-multicast/src/test/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryTest.java
@@ -0,0 +1,124 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.multicast;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertTrue;

+

+import java.net.MulticastSocket;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.atomic.AtomicReference;

+

+import junit.framework.Assert;

+

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+

+/**

+ * MulticastRegistryTest

+ * 

+ * @author tony.chenl

+ */

+public class MulticastRegistryTest {

+

+    String            service     = "com.alibaba.dubbo.test.injvmServie";

+    URL               registryUrl = URL.valueOf("multicast://239.255.255.255/");

+    URL               serviceUrl  = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + "/" + service

+                                                + "?methods=test1,test2");

+    URL               consumerUrl = URL.valueOf("subscribe://" + NetUtils.getLocalHost() + "/" + service + "?arg1=1&arg2=2");

+    MulticastRegistry registry    = new MulticastRegistry(registryUrl);

+

+    /**

+     * @throws java.lang.Exception

+     */

+    @BeforeClass

+    public static void setUpBeforeClass() throws Exception {

+    }

+

+    /**

+     * @throws java.lang.Exception

+     */

+    @Before

+    public void setUp() throws Exception {

+        registry.register(serviceUrl, null);

+    }

+

+    @Test(expected = IllegalArgumentException.class)

+    public void testUrlerror() {

+        URL errorUrl = URL.valueOf("multicast://mullticast/");

+        new MulticastRegistry(errorUrl);

+    }

+

+    /**

+     * Test method for {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#register(java.util.Map, NotifyListener)}.

+     */

+    @Test

+    public void testRegister() {

+        Set<String> registered = null;

+        // clear first

+        registered = registry.getRegistered();

+

+        for (int i = 0; i < 2; i++) {

+            registry.register(serviceUrl, null);

+            registered = registry.getRegistered();

+            assertTrue(registered.contains(serviceUrl.toFullString()));

+        }

+        // confirm only 1 regist success;

+        registered = registry.getRegistered();

+        assertEquals(1, registered.size());

+    }

+

+    /**

+     * Test method for

+     * {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#subscribe(java.util.Map, com.alibaba.dubbo.registry.support.NotifyListener)}

+     * .

+     */

+    @Test

+    public void testSubscribe() {

+        // verify lisener.

+        final AtomicReference<URL> args = new AtomicReference<URL>();

+        registry.subscribe(consumerUrl, new NotifyListener() {

+

+            public void notify(List<URL> urls) {

+                // FIXME assertEquals(MulticastRegistry.this.service, service);

+                args.set(urls.get(0));

+            }

+        });

+        assertEquals(serviceUrl.toFullString(), args.get().toFullString());

+        Map<String, Set<NotifyListener>> arg = registry.getSubscribed();

+        assertEquals(consumerUrl.toFullString(), arg.keySet().iterator().next());

+

+    }

+

+    @Test

+    public void testDefaultPort() {

+        MulticastRegistry multicastRegistry = new MulticastRegistry(URL.valueOf("multicast://224.5.6.7"));

+        try {

+            MulticastSocket multicastSocket = multicastRegistry.getMutilcastSocket();

+            Assert.assertEquals(1234, multicastSocket.getLocalPort());

+        } finally {

+            multicastRegistry.destroy();

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-redis/pom.xml b/dubbo-registry-redis/pom.xml
new file mode 100644
index 0000000..fd2796c
--- /dev/null
+++ b/dubbo-registry-redis/pom.xml
@@ -0,0 +1,43 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-registry-redis</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Redis Registry Module</name>
+	<description>The redis registry module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-registry</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>

+			<groupId>redis.clients</groupId>

+			<artifactId>jedis</artifactId>

+			<scope>provided</scope>

+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java b/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
new file mode 100644
index 0000000..251d35f
--- /dev/null
+++ b/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
@@ -0,0 +1,514 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.registry.redis;

+

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.Collection;

+import java.util.Date;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Random;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import org.apache.commons.pool.impl.GenericObjectPool;

+

+import redis.clients.jedis.Jedis;

+import redis.clients.jedis.JedisPool;

+import redis.clients.jedis.JedisPubSub;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.common.utils.UrlUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.support.FailbackRegistry;

+

+/**

+ * RedisRegistry

+ * 

+ * @author william.liangf

+ */

+public class RedisRegistry extends FailbackRegistry {

+

+    private static final Logger logger = LoggerFactory.getLogger(RedisRegistry.class);

+

+    private static final int DEFAULT_REDIS_PORT = 6379;

+

+    private final static String DEFAULT_ROOT = "dubbo";

+

+    private final ScheduledExecutorService expireExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryExpireTimer", true));

+

+    private final ScheduledFuture<?> expireFuture;

+    

+    private final String root;

+

+    private final JedisPool jedisPool;

+

+    private final NotifySub sub = new NotifySub();

+    

+    private final ConcurrentMap<String, Notifier> notifiers = new ConcurrentHashMap<String, Notifier>();

+    

+    private final int reconnectPeriod;

+

+    private final int expirePeriod;

+    

+    private volatile boolean admin = false;

+

+    public RedisRegistry(URL url) {

+        super(url);

+        GenericObjectPool.Config config = new GenericObjectPool.Config();

+        config.testOnBorrow = url.getParameter("test.on.borrow", true);

+        config.testOnReturn = url.getParameter("test.on.return", false);

+        config.testWhileIdle = url.getParameter("test.while.idle", false);

+        if (url.getParameter("max.idle", 0) > 0)

+            config.maxIdle = url.getParameter("max.idle", 0);

+        if (url.getParameter("min.idle", 0) > 0)

+            config.minIdle = url.getParameter("min.idle", 0);

+        if (url.getParameter("max.active", 0) > 0)

+            config.maxActive = url.getParameter("max.active", 0);

+        if (url.getParameter("max.wait", 0) > 0)

+            config.maxWait = url.getParameter("max.wait", 0);

+        if (url.getParameter("num.tests.per.eviction.run", 0) > 0)

+            config.numTestsPerEvictionRun = url.getParameter("num.tests.per.eviction.run", 0);

+        if (url.getParameter("time.between.eviction.runs.millis", 0) > 0)

+            config.timeBetweenEvictionRunsMillis = url.getParameter("time.between.eviction.runs.millis", 0);

+        if (url.getParameter("min.evictable.idle.time.millis", 0) > 0)

+            config.minEvictableIdleTimeMillis = url.getParameter("min.evictable.idle.time.millis", 0);

+        

+        this.jedisPool = new JedisPool(config, url.getHost(), 

+                url.getPort() == 0 ? DEFAULT_REDIS_PORT : url.getPort(), 

+                url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));

+        this.reconnectPeriod = url.getParameter(Constants.REGISTRY_RECONNECT_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RECONNECT_PERIOD);

+        String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);

+        if (! group.startsWith(Constants.PATH_SEPARATOR)) {

+            group = Constants.PATH_SEPARATOR + group;

+        }

+        if (! group.endsWith(Constants.PATH_SEPARATOR)) {

+            group = group + Constants.PATH_SEPARATOR;

+        }

+        this.root = group;

+        

+        this.expirePeriod = url.getParameter(Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT);

+        this.expireFuture = expireExecutor.scheduleWithFixedDelay(new Runnable() {

+            public void run() {

+                try {

+                    deferExpired(); // 延长过期时间

+                } catch (Throwable t) { // 防御性容错

+                    logger.error("Unexpected error occur at defer expire time, cause: " + t.getMessage(), t);

+                }

+            }

+        }, expirePeriod / 2, expirePeriod / 2, TimeUnit.MILLISECONDS);

+    }

+    

+    private void deferExpired() {

+        Jedis jedis = jedisPool.getResource();

+        try {

+            for (String provider : new HashSet<String>(getRegistered())) {

+                String key = toProviderPath(URL.valueOf(provider));

+                if (jedis.hset(key, provider, String.valueOf(System.currentTimeMillis() + expirePeriod)) == 0) {

+                    jedis.publish(key, Constants.REGISTER);

+                }

+            }

+            for (String consumer : new HashSet<String>(getSubscribed().keySet())) {

+                URL url = URL.valueOf(consumer);

+                if (! Constants.ANY_VALUE.equals(url.getServiceInterface())) {

+                    String key = toConsumerPath(url);

+                    if (jedis.hset(key, consumer, String.valueOf(System.currentTimeMillis() + expirePeriod)) == 0) {

+                        jedis.publish(key, Constants.SUBSCRIBE);

+                    }

+                }

+            }

+            if (admin) {

+                clean(jedis);

+            }

+        } finally {

+            jedisPool.returnResource(jedis);

+        }

+    }

+    

+    // 监控中心负责删除过期脏数据

+    private void clean(Jedis jedis) {

+        Set<String> keys = jedis.keys(root + Constants.ANY_VALUE);

+        if (keys != null && keys.size() > 0) {

+            for (String key : keys) {

+                Map<String, String> values = jedis.hgetAll(key);

+                if (values != null && values.size() > 0) {

+                    boolean delete = false;

+                    long now = System.currentTimeMillis();

+                    for (Map.Entry<String, String> entry : values.entrySet()) {

+                        long expire = Long.parseLong(entry.getValue());

+                        if (expire < now) {

+                            jedis.hdel(key, entry.getKey());

+                            delete = true;

+                            if (logger.isWarnEnabled()) {

+                                logger.warn("Delete expired key: " + key + " -> value: " + entry.getKey() + ", expire: " + new Date(expire) + ", now: " + new Date(now));

+                            }

+                        }

+                    }

+                    if (delete) {

+                        if (key.endsWith(Constants.CONSUMERS)) {

+                            jedis.publish(key, Constants.UNSUBSCRIBE);

+                        } else {

+                            jedis.publish(key, Constants.UNREGISTER);

+                        }

+                    }

+                }

+            }

+        }

+    }

+

+    public boolean isAvailable() {

+        try {

+            Jedis jedis = jedisPool.getResource();

+            try {

+                return jedis.isConnected();

+            } finally {

+                jedisPool.returnResource(jedis);

+            }

+        } catch (Throwable t) {

+            return false;

+        }

+    }

+

+    @Override

+    public void destroy() {

+        super.destroy();

+        try {

+            expireFuture.cancel(true);

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+        try {

+            for (Notifier notifier : notifiers.values()) {

+                notifier.shutdown();

+            }

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+        try {

+            jedisPool.destroy();

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+    }

+

+    @Override

+    public void doRegister(URL url) {

+        String key = toProviderPath(url);

+        String value = url.toFullString();

+        String expire = String.valueOf(System.currentTimeMillis() + expirePeriod);

+        Jedis jedis = jedisPool.getResource();

+        try {

+            jedis.hset(key, value, expire);

+            jedis.publish(key, Constants.REGISTER);

+        } finally {

+            jedisPool.returnResource(jedis);

+        }

+    }

+

+    @Override

+    public void doUnregister(URL url) {

+        String key = toProviderPath(url);

+        String value = url.toFullString();

+        Jedis jedis = jedisPool.getResource();

+        try {

+            jedis.hdel(key, value);

+            jedis.publish(key, Constants.UNREGISTER);

+        } finally {

+            jedisPool.returnResource(jedis);

+        }

+    }

+    

+    @Override

+    public void doSubscribe(final URL url, final NotifyListener listener) {

+        String service = toServicePath(url);

+        Notifier notifier = notifiers.get(service);

+        if (notifier == null) {

+            Notifier newNotifier = new Notifier(service);

+            notifiers.putIfAbsent(service, newNotifier);

+            notifier = notifiers.get(service);

+            if (notifier == newNotifier) {

+                notifier.start();

+            }

+        }

+        Jedis jedis = jedisPool.getResource();

+        try {

+            if (service.endsWith(Constants.ANY_VALUE)) {

+                admin = true;

+                for (String s : getServices(jedis, service)) {

+                    doNotify(jedis, s, url, listener);

+                }

+            } else {

+                String key = toConsumerPath(url);

+                String value = url.toFullString();

+                String expire = String.valueOf(System.currentTimeMillis() + expirePeriod);

+                jedis.hset(key, value, expire);

+                jedis.publish(key, Constants.SUBSCRIBE);

+                doNotify(jedis, service, url, listener);

+            }

+        } finally {

+            jedisPool.returnResource(jedis);

+        }

+    }

+

+    @Override

+    public void doUnsubscribe(URL url, NotifyListener listener) {

+        if (! Constants.ANY_VALUE.equals(url.getServiceInterface())) {

+            String key = toConsumerPath(url);

+            String value = url.toFullString();

+            Jedis jedis = jedisPool.getResource();

+            try {

+                jedis.hdel(key, value);

+                jedis.publish(key, Constants.UNSUBSCRIBE);

+            } finally {

+                jedisPool.returnResource(jedis);

+            }

+        }

+    }

+

+    private void doNotify(Jedis jedis, String service, boolean consumer) {

+        for (Map.Entry<String, Set<NotifyListener>> entry : new HashMap<String, Set<NotifyListener>>(getSubscribed()).entrySet()) {

+            URL url = URL.valueOf(entry.getKey());

+            if (Constants.ANY_VALUE.equals(url.getServiceInterface()) 

+                    || (! consumer && toServicePath(url).equals(service))) {

+                doNotify(jedis, service, url, new HashSet<NotifyListener>(entry.getValue()));

+            }

+        }

+    }

+    

+    private void doNotify(Jedis jedis, String service, URL url, NotifyListener listener) {

+        doNotify(jedis, service, url, Arrays.asList(listener));

+    }

+

+    private void doNotify(Jedis jedis, String service, URL url, Collection<NotifyListener> listeners) {

+        Map<String, String> providers;

+        providers = jedis.hgetAll(service + Constants.PATH_SEPARATOR + Constants.PROVIDERS);

+        if (url.getParameter(Constants.ADMIN_KEY, false)) {

+            Map<String, String> consumers = jedis.hgetAll(service + Constants.PATH_SEPARATOR + Constants.CONSUMERS);

+            if (consumers != null && consumers.size() > 0) {

+                providers = providers == null ? new HashMap<String, String>() : new HashMap<String, String>(providers);

+                providers.putAll(consumers);

+            }

+        }

+        if (logger.isInfoEnabled()) {

+            logger.info("redis notify: " + service + " = " + providers);

+        }

+        

+        List<URL> urls = new ArrayList<URL>();

+        if (providers != null && providers.size() > 0) {

+            long now = System.currentTimeMillis();

+            for (Map.Entry<String, String> entry : providers.entrySet()) {

+                URL u = URL.valueOf(entry.getKey());

+                if (Long.parseLong(entry.getValue()) >= now) {

+                    if (UrlUtils.isMatch(url, u)) {

+                        urls.add(u);

+                    }

+                }

+            }

+        }

+        if (urls != null && urls.isEmpty() && url.getParameter(Constants.ADMIN_KEY, false)) {

+            URL empty = url.setProtocol(Constants.EMPTY_PROTOCOL);

+            if (Constants.ANY_VALUE.equals(empty.getServiceInterface())) {

+                empty = empty.setServiceInterface(service.substring(root.length()));

+            }

+            urls.add(empty);

+        }

+        for (NotifyListener listener : listeners) {

+            notify(url, listener, urls);

+        }

+    }

+    

+    private String getService(Jedis jedis, String key) {

+        int i = key.indexOf(Constants.PATH_SEPARATOR, root.length());

+        return i > 0 ? key.substring(0, i) : key;

+    }

+

+    private Set<String> getServices(Jedis jedis, String pattern) {

+        Set<String> keys = jedis.keys(pattern);

+        Set<String> services = new HashSet<String>();

+        if (keys != null && keys.size() > 0) {

+            for (String key : keys) {

+                services.add(getService(jedis, key));

+            }

+        }

+        return services;

+    }

+

+    private String toServicePath(URL url) {

+        return root + url.getServiceInterface();

+    }

+

+    private String toProviderPath(URL url) {

+        return toServicePath(url) + Constants.PATH_SEPARATOR + Constants.PROVIDERS;

+    }

+

+    private String toConsumerPath(URL url) {

+        return toServicePath(url) + Constants.PATH_SEPARATOR + Constants.CONSUMERS;

+    }

+

+    private class NotifySub extends JedisPubSub {

+

+        @Override

+        public void onMessage(String key, String msg) {

+            if (logger.isInfoEnabled()) {

+                logger.info("redis event: " + key + " = " + msg);

+            }

+            if (msg.equals(Constants.REGISTER) 

+                    || msg.equals(Constants.UNREGISTER)

+                    || msg.equals(Constants.SUBSCRIBE) 

+                    || msg.equals(Constants.UNSUBSCRIBE)) {

+                Jedis jedis = jedisPool.getResource();

+                try {

+                    doNotify(jedis, getService(jedis, key), msg.equals(Constants.SUBSCRIBE) || msg.equals(Constants.UNSUBSCRIBE));

+                } finally {

+                    jedisPool.returnResource(jedis);

+                }

+            }

+        }

+

+        @Override

+        public void onPMessage(String pattern, String key, String msg) {

+            onMessage(key, msg);

+        }

+

+        @Override

+        public void onSubscribe(String key, int num) {

+        }

+

+        @Override

+        public void onPSubscribe(String pattern, int num) {

+        }

+

+        @Override

+        public void onUnsubscribe(String key, int num) {

+        }

+

+        @Override

+        public void onPUnsubscribe(String pattern, int num) {

+        }

+

+    }

+

+    private class Notifier extends Thread {

+

+        private final String service;

+

+        private volatile Jedis jedis;

+

+        private volatile boolean first = true;

+        

+        private volatile boolean running = true;

+        

+        private final AtomicInteger connectSkip = new AtomicInteger();

+

+        private final AtomicInteger connectSkiped = new AtomicInteger();

+

+        private final Random random = new Random();

+        

+        private volatile int connectRandom;

+

+        private void resetSkip() {

+            connectSkip.set(0);

+            connectSkiped.set(0);

+            connectRandom = 0;

+        }

+        

+        private boolean isSkip() {

+            int skip = connectSkip.get(); // 跳过次数增长

+            if (skip >= 10) { // 如果跳过次数增长超过10,取随机数

+                if (connectRandom == 0) {

+                    connectRandom = random.nextInt(10);

+                }

+                skip = 10 + connectRandom;

+            }

+            if (connectSkiped.getAndIncrement() < skip) { // 检查跳过次数

+                return true;

+            }

+            connectSkip.incrementAndGet();

+            connectSkiped.set(0);

+            connectRandom = 0;

+            return false;

+        }

+        

+        public Notifier(String service) {

+            super.setDaemon(true);

+            super.setName("DubboRedisSubscribe");

+            this.service = service;

+        }

+        

+        @Override

+        public void run() {

+            while (running) {

+                try {

+                    if (! isSkip()) {

+                        try {

+                            jedis = jedisPool.getResource();

+                            try {

+                                if (service.endsWith(Constants.ANY_VALUE)) {

+                                    if (! first) {

+                                        first = false;

+                                        for (String s : getServices(jedis, service)) {

+                                            doNotify(jedis, s, false);

+                                        }

+                                        resetSkip();

+                                    }

+                                    jedis.psubscribe(sub, service);

+                                } else {

+                                    if (! first) {

+                                        first = false;

+                                        doNotify(jedis, service, false);

+                                        resetSkip();

+                                    }

+                                    jedis.subscribe(sub, service + Constants.PATH_SEPARATOR + Constants.PROVIDERS);

+                                }

+                            } finally {

+                                jedisPool.returnBrokenResource(jedis);

+                            }

+                        } catch (Throwable t) {

+                            logger.error(t.getMessage(), t);

+                            sleep(reconnectPeriod);

+                        }

+                    }

+                } catch (Throwable t) {

+                    logger.error(t.getMessage(), t);

+                }

+            }

+        }

+        

+        public void shutdown() {

+            try {

+                running = false;

+                jedis.disconnect();

+            } catch (Throwable t) {

+                logger.warn(t.getMessage(), t);

+            }

+        }

+        

+    }

+

+}

diff --git a/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistryFactory.java b/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistryFactory.java
new file mode 100644
index 0000000..52825c0
--- /dev/null
+++ b/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistryFactory.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.registry.redis;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.RegistryFactory;

+

+/**

+ * RedisRegistryFactory

+ * 

+ * @author william.liangf

+ */

+public class RedisRegistryFactory implements RegistryFactory {

+

+    public Registry getRegistry(URL url) {

+        return new RedisRegistry(url);

+    }

+

+}

diff --git a/dubbo-registry-redis/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-redis/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..5ff2352
--- /dev/null
+++ b/dubbo-registry-redis/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+redis=com.alibaba.dubbo.registry.redis.RedisRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry-redis/src/test/java/com/alibaba/dubbo/registry/redis/RedisRegistryTest.java b/dubbo-registry-redis/src/test/java/com/alibaba/dubbo/registry/redis/RedisRegistryTest.java
new file mode 100644
index 0000000..b707901
--- /dev/null
+++ b/dubbo-registry-redis/src/test/java/com/alibaba/dubbo/registry/redis/RedisRegistryTest.java
@@ -0,0 +1,96 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.registry.redis;

+

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.registry.NotifyListener;

+

+/**

+ * RedisRegistryTest

+ * 

+ * @author tony.chenl

+ */

+public class RedisRegistryTest {

+

+    String            service     = "com.alibaba.dubbo.test.injvmServie";

+    URL               registryUrl = URL.valueOf("redis://239.255.255.255/");

+    URL               serviceUrl  = URL.valueOf("redis://redis/" + service

+                                                + "?notify=false&methods=test1,test2");

+    URL               consumerUrl = URL.valueOf("redis://consumer/" + service + "?notify=false&methods=test1,test2");

+    // RedisRegistry registry    = new RedisRegistry(registryUrl);

+

+    /**

+     * @throws java.lang.Exception

+     */

+    @BeforeClass

+    public static void setUpBeforeClass() throws Exception {

+    }

+

+    /**

+     * @throws java.lang.Exception

+     */

+    @Before

+    public void setUp() throws Exception {

+        //registry.register(service, serviceUrl);

+    }

+

+    /**

+     * Test method for {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#register(java.util.Map, NotifyListener)}.

+     */

+    @Test

+    public void testRegister() {

+        /*List<URL> registered = null;

+        // clear first

+        registered = registry.getRegistered(service);

+

+        for (int i = 0; i < 2; i++) {

+            registry.register(service, serviceUrl);

+            registered = registry.getRegistered(service);

+            assertTrue(registered.contains(serviceUrl));

+        }

+        // confirm only 1 regist success;

+        registered = registry.getRegistered(service);

+        assertEquals(1, registered.size());*/

+    }

+

+    /**

+     * Test method for

+     * {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#subscribe(java.util.Map, com.alibaba.dubbo.registry.support.NotifyListener)}

+     * .

+     */

+    @Test

+    public void testSubscribe() {

+        /*final String subscribearg = "arg1=1&arg2=2";

+        // verify lisener.

+        final AtomicReference<Map<String, String>> args = new AtomicReference<Map<String, String>>();

+        registry.subscribe(service, new URL("dubbo", NetUtils.getLocalHost(), 0, StringUtils.parseQueryString(subscribearg)), new NotifyListener() {

+

+            public void notify(List<URL> urls) {

+                // FIXME assertEquals(RedisRegistry.this.service, service);

+                args.set(urls.get(0).getParameters());

+            }

+        });

+        assertEquals(serviceUrl.toParameterString(), StringUtils.toQueryString(args.get()));

+        Map<String, String> arg = registry.getSubscribed(service);

+        assertEquals(subscribearg, StringUtils.toQueryString(arg));*/

+

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/pom.xml b/dubbo-registry-simple/pom.xml
new file mode 100644
index 0000000..dc017d9
--- /dev/null
+++ b/dubbo-registry-simple/pom.xml
@@ -0,0 +1,84 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-registry-simple</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Simple Registry Module</name>
+	<description>The simple registry module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>false</skip_maven_deploy>

+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>

+		<dependency>

+			<groupId>org.mortbay.jetty</groupId>

+			<artifactId>jetty</artifactId>

+		</dependency>
+	</dependencies>

+	<build>

+	     <plugins>

+			<plugin>

+				<artifactId>maven-dependency-plugin</artifactId>

+				<executions>

+					<execution>

+						<id>unpack</id>

+						<phase>package</phase>

+						<goals>

+							<goal>unpack</goal>

+						</goals>

+						<configuration>

+							<artifactItems>

+								<artifactItem>

+									<groupId>com.alibaba</groupId>

+									<artifactId>dubbo</artifactId>

+									<version>${project.parent.version}</version>

+									<outputDirectory>${project.build.directory}/dubbo</outputDirectory>

+									<includes>META-INF/assembly/**</includes>

+								</artifactItem>

+							</artifactItems>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+			<plugin>

+				<artifactId>maven-assembly-plugin</artifactId>

+                <configuration>

+                    <descriptor>src/main/assembly/assembly.xml</descriptor>

+                </configuration>

+                <executions>

+					<execution>

+						<id>make-assembly</id>

+						<phase>package</phase>

+						<goals>

+							<goal>single</goal>

+						</goals>

+					</execution>

+				</executions>

+            </plugin>

+		</plugins>

+	</build>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/main/assembly/assembly.xml b/dubbo-registry-simple/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..25235d9
--- /dev/null
+++ b/dubbo-registry-simple/src/main/assembly/assembly.xml
@@ -0,0 +1,39 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<assembly>

+	<id>assembly</id>

+	<formats>

+		<format>tar.gz</format>

+	</formats>

+	<includeBaseDirectory>true</includeBaseDirectory>

+	<fileSets>

+		<fileSet>

+			<directory>${project.build.directory}/dubbo/META-INF/assembly/bin</directory>

+			<outputDirectory>bin</outputDirectory>

+			<fileMode>0755</fileMode>

+		</fileSet>

+		<fileSet>

+			<directory>src/main/assembly/conf</directory>

+			<outputDirectory>conf</outputDirectory>

+			<fileMode>0644</fileMode>

+		</fileSet>

+	</fileSets>

+	<dependencySets>

+		<dependencySet>

+			<outputDirectory>lib</outputDirectory>

+		</dependencySet>

+	</dependencySets>

+</assembly>
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/main/assembly/conf/dubbo.properties b/dubbo-registry-simple/src/main/assembly/conf/dubbo.properties
new file mode 100644
index 0000000..6e6c63e
--- /dev/null
+++ b/dubbo-registry-simple/src/main/assembly/conf/dubbo.properties
@@ -0,0 +1,21 @@
+##

+# Copyright 1999-2011 Alibaba Group.

+#  

+# 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.

+##

+dubbo.container=log4j,spring

+dubbo.application.name=simple-registry

+dubbo.application.owner=

+dubbo.protocol.port=9090

+dubbo.log4j.file=logs/dubbo-simple-registry.log

+dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/simple/SimpleRegistryService.java b/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/simple/SimpleRegistryService.java
new file mode 100644
index 0000000..415b504
--- /dev/null
+++ b/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/simple/SimpleRegistryService.java
@@ -0,0 +1,190 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.simple;

+

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.UrlUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.RegistryService;

+import com.alibaba.dubbo.registry.support.FailbackRegistry;

+import com.alibaba.dubbo.rpc.RpcContext;

+

+/**

+ * DubboRegistryService

+ * 

+ * @author william.liangf

+ */

+public class SimpleRegistryService extends FailbackRegistry {

+

+    private final ConcurrentMap<String, Set<String>> remoteRegistered = new ConcurrentHashMap<String, Set<String>>();

+

+    private final ConcurrentMap<String, ConcurrentMap<String, Set<NotifyListener>>> remoteSubscribed = new ConcurrentHashMap<String, ConcurrentMap<String, Set<NotifyListener>>>();

+    

+    private final static Logger logger = LoggerFactory.getLogger(SimpleRegistryService.class);

+

+    public SimpleRegistryService() {

+        super(new URL("dubbo", NetUtils.getLocalHost(), 0, RegistryService.class.getName()));

+    }

+

+    public boolean isAvailable() {

+        return true;

+    }

+    

+    public void register(URL url, NotifyListener listener) {

+        String client = RpcContext.getContext().getRemoteAddressString();

+        Set<String> urls = remoteRegistered.get(client);

+        if (urls == null) {

+            remoteRegistered.putIfAbsent(client, new ConcurrentHashSet<String>());

+            urls = remoteRegistered.get(client);

+        }

+        urls.add(url.toFullString());

+        super.register(url, listener);

+        registered(url);

+    }

+

+    public void unregister(URL url, NotifyListener listener) {

+        String client = RpcContext.getContext().getRemoteAddressString();

+        Set<String> urls = remoteRegistered.get(client);

+        if (urls != null && urls.size() > 0) {

+            urls.remove(url.toFullString());

+        }

+        super.unregister(url, listener);

+        unregistered(url);

+    }

+

+    public void subscribe(URL url, NotifyListener listener) {

+        if (getUrl().getPort() == 0) {

+            URL registryUrl = RpcContext.getContext().getInvoker().getUrl();

+            if (registryUrl != null && registryUrl.getPort() > 0) {

+                super.setUrl(registryUrl);

+                super.register(registryUrl, null);

+            }

+        }

+        if (! Constants.ANY_VALUE.equals(url.getServiceInterface())

+                && url.getParameter(Constants.REGISTER_KEY, true)) {

+            register(url, null);

+        }

+        String client = RpcContext.getContext().getRemoteAddressString();

+        ConcurrentMap<String, Set<NotifyListener>> clientListeners = remoteSubscribed.get(client);

+        if (clientListeners == null) {

+            remoteSubscribed.putIfAbsent(client, new ConcurrentHashMap<String, Set<NotifyListener>>());

+            clientListeners = remoteSubscribed.get(client);

+        }

+        String key = url.toFullString();

+        Set<NotifyListener> listeners = clientListeners.get(key);

+        if (listeners == null) {

+            clientListeners.putIfAbsent(key, new ConcurrentHashSet<NotifyListener>());

+            listeners = clientListeners.get(key);

+        }

+        listeners.add(listener);

+        super.subscribe(url, listener);

+        subscribed(url, listener);

+    }

+

+    public void unsubscribe(URL url, NotifyListener listener) {

+        if (! Constants.ANY_VALUE.equals(url.getServiceInterface())

+                && url.getParameter(Constants.REGISTER_KEY, true)) {

+            unregister(url, null);

+        }

+        String client = RpcContext.getContext().getRemoteAddressString();

+        Map<String, Set<NotifyListener>> clientListeners = remoteSubscribed.get(client);

+        if (clientListeners != null && clientListeners.size() > 0) {

+            String key = url.toFullString();

+            Set<NotifyListener> listeners = clientListeners.get(key);

+            if (listeners != null && listeners.size() > 0) {

+                listeners.remove(listener);

+            }

+        }

+        super.unregister(url, null);

+    }

+

+    protected void registered(URL url) {

+        for (Map.Entry<String, Set<NotifyListener>> entry : getSubscribed().entrySet()) {

+            String key = entry.getKey();

+            URL subscribe = URL.valueOf(key);

+            if (UrlUtils.isMatch(subscribe, url)) {

+                List<URL> list = lookup(subscribe);

+                for (NotifyListener listener : entry.getValue()) {

+                    notify(subscribe, listener, list);

+                }

+            }

+        }

+    }

+

+    protected void unregistered(URL url) {

+        for (Map.Entry<String, Set<NotifyListener>> entry : getSubscribed().entrySet()) {

+            String key = entry.getKey();

+            URL subscribe = URL.valueOf(key);

+            if (UrlUtils.isMatch(subscribe, url)) {

+                List<URL> list = lookup(subscribe);

+                for (NotifyListener listener : entry.getValue()) {

+                    notify(subscribe, listener, list);

+                }

+            }

+        }

+    }

+

+    protected void subscribed(URL url, NotifyListener listener) {

+        List<URL> urls = lookup(url);

+        notify(url, listener, urls);

+    }

+

+    public void doRegister(URL url) {

+    }

+    

+    public void doUnregister(URL url) {

+    }

+    

+    public void doSubscribe(URL url, NotifyListener listener) {

+    }

+    

+    public void doUnsubscribe(URL url, NotifyListener listener) {

+    }

+    

+    public void disconnect() {

+        String client = RpcContext.getContext().getRemoteAddressString();

+        if (logger.isInfoEnabled()) {

+            logger.info("Disconnected " + client);

+        }

+        Set<String> urls = remoteRegistered.get(client);

+        if (urls != null && urls.size() > 0) {

+            for (String url : urls) {

+                unregister(URL.valueOf(url), null);

+            }

+        }

+        Map<String, Set<NotifyListener>> listeners = remoteSubscribed.get(client);

+        if (listeners != null && listeners.size() > 0) {

+            for (Map.Entry<String, Set<NotifyListener>> entry : listeners.entrySet()) {

+                String url = entry.getKey();

+                for (NotifyListener listener : entry.getValue()) {

+                    unsubscribe(URL.valueOf(url), listener);

+                }

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/main/resources/META-INF/spring/dubbo-registry-simple.xml b/dubbo-registry-simple/src/main/resources/META-INF/spring/dubbo-registry-simple.xml
new file mode 100644
index 0000000..561adb3
--- /dev/null
+++ b/dubbo-registry-simple/src/main/resources/META-INF/spring/dubbo-registry-simple.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<beans xmlns="http://www.springframework.org/schema/beans"

+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"

+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

+    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

+

+	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

+        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />

+        <property name="location" value="classpath:dubbo.properties" />

+    </bean>

+	

+    <dubbo:application name="${dubbo.application.name}" owner="${dubbo.application.owner}" />

+    

+    <dubbo:protocol name="dubbo" port="${dubbo.protocol.port}" heartbeat="180000" />

+    

+    <dubbo:service id="registryServiceConfig" interface="com.alibaba.dubbo.registry.RegistryService" ref="registryService" registry="N/A" ondisconnect="disconnect" callbacks="1000">

+        <dubbo:method name="subscribe"><dubbo:argument index="1" callback="true" /></dubbo:method>

+        <dubbo:method name="unsubscribe"><dubbo:argument index="1" callback="false" /></dubbo:method>

+    </dubbo:service>

+    

+    <bean id="registryService" class="com.alibaba.dubbo.registry.simple.SimpleRegistryService" />

+

+</beans>
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistry.java b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistry.java
new file mode 100644
index 0000000..efa518a
--- /dev/null
+++ b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistry.java
@@ -0,0 +1,24 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.simple;

+

+public class SimpleRegistry {

+    

+    public static void main(String[] args) {

+        com.alibaba.dubbo.container.Main.main(args);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistryServiceTest.java b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistryServiceTest.java
new file mode 100644
index 0000000..76e19b7
--- /dev/null
+++ b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistryServiceTest.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.simple;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.registry.simple.SimpleRegistryService;

+

+/**

+ * SimpleRegistryServiceTest

+ * 

+ * @author william.liangf

+ */

+public class SimpleRegistryServiceTest {

+

+    @Test

+    public void testRegistry() {

+        new SimpleRegistryService();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/test/resources/dubbo.properties b/dubbo-registry-simple/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..246fc36
--- /dev/null
+++ b/dubbo-registry-simple/src/test/resources/dubbo.properties
@@ -0,0 +1,21 @@
+##

+# Copyright 1999-2011 Alibaba Group.

+#  

+# 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.

+##

+dubbo.container=log4j,spring

+dubbo.application.name=simple-registry

+dubbo.application.owner=

+dubbo.protocol.port=9090

+#dubbo.log4j.file=logs/dubbo-demo-consumer.log

+#dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/test/resources/log4j.xml b/dubbo-registry-simple/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-registry-simple/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">

+	<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

+		<layout class="org.apache.log4j.PatternLayout">

+			<param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />

+		</layout>

+	</appender>

+	<root>

+		<level value="INFO" />

+		<appender-ref ref="CONSOLE" />

+	</root>

+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-registry-zookeeper/pom.xml b/dubbo-registry-zookeeper/pom.xml
new file mode 100644
index 0000000..b4bfcb2
--- /dev/null
+++ b/dubbo-registry-zookeeper/pom.xml
@@ -0,0 +1,42 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-registry-zookeeper</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Zookeeper Registry Module</name>
+	<description>The zookeeper registry module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-registry</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+		    <groupId>org.apache.zookeeper</groupId>
+		    <artifactId>zookeeper</artifactId>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java
new file mode 100644
index 0000000..e3d45c9
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java
@@ -0,0 +1,478 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.zookeeper;

+

+import java.util.ArrayList;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.locks.ReentrantLock;

+

+import org.apache.zookeeper.CreateMode;

+import org.apache.zookeeper.KeeperException;

+import org.apache.zookeeper.KeeperException.NoNodeException;

+import org.apache.zookeeper.KeeperException.NodeExistsException;

+import org.apache.zookeeper.WatchedEvent;

+import org.apache.zookeeper.Watcher;

+import org.apache.zookeeper.Watcher.Event.EventType;

+import org.apache.zookeeper.Watcher.Event.KeeperState;

+import org.apache.zookeeper.ZooDefs.Ids;

+import org.apache.zookeeper.ZooKeeper;

+import org.apache.zookeeper.data.ACL;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.common.utils.UrlUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.support.FailbackRegistry;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * ZookeeperRegistry

+ * 

+ * @author william.liangf

+ */

+public class ZookeeperRegistry extends FailbackRegistry {

+

+    private final static Logger logger = LoggerFactory.getLogger(ZookeeperRegistry.class);

+

+    private final static int DEFAULT_ZOOKEEPER_PORT = 2181;

+    

+    private final static String DEFAULT_ROOT = "dubbo";

+

+    private final String        root;

+    

+    private final boolean       auth;

+    

+    private final List<ACL>     acl;

+

+    private final ReentrantLock zookeeperLock = new ReentrantLock();

+

+    private final Set<String> failedWatched = new ConcurrentHashSet<String>();

+

+    private final Set<String> anyServices = new ConcurrentHashSet<String>();

+    

+    private final ConcurrentMap<String, Set<NotifyListener>> anyNotifyListeners = new ConcurrentHashMap<String, Set<NotifyListener>>();

+    

+    private volatile ZooKeeper  zookeeper;

+

+    public ZookeeperRegistry(URL url) {

+        super(url);

+        this.auth = url.getUsername() != null && url.getUsername().length() > 0 

+                && url.getPassword() != null && url.getPassword().length() > 0;

+        this.acl = auth ? Ids.CREATOR_ALL_ACL : Ids.OPEN_ACL_UNSAFE;

+        String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);

+        if (! group.startsWith(Constants.PATH_SEPARATOR)) {

+            group = Constants.PATH_SEPARATOR + group;

+        }

+        this.root = group;

+        initZookeeper();

+    }

+

+    @Override

+    protected void doRetry() {

+        initZookeeper();

+        if (failedWatched.size() > 0) {

+            Set<String> failed = new HashSet<String>(failedWatched);

+            if (failed.size() > 0) {

+                if (logger.isInfoEnabled()) {

+                    logger.info("Retry watch " + failed + " to zookeeper " + getUrl());

+                }

+                for (String service : failed) {

+                    try {

+                        getChildren(service);

+                        failedWatched.remove(service);

+                    } catch (Throwable t) {

+                        logger.warn("Failed to retry register " + failed + " to zookeeper " + getUrl() + ", waiting for again, cause: " + t.getMessage(), t);

+                    }

+                }

+            }

+        }

+    }

+    

+    private List<String> watch(String service) {

+        try {

+            ZooKeeper zk = ZookeeperRegistry.this.zookeeper;

+            if (zk != null) {

+                List<String> result = getChildren(service);

+                failedWatched.remove(service);

+                return result;

+            }

+        } catch (Throwable e) {

+            logger.warn("Failed to watch path " + service + " to zookeeper" + getUrl() + ", cause: " + e.getMessage(), e);

+        }

+        failedWatched.add(service);

+        return new ArrayList<String>(0);

+    }

+    

+    private void initZookeeper() {

+        ZooKeeper zk = this.zookeeper;

+        if (zk == null || zk.getState() == null || ! zk.getState().isAlive()) {

+            zookeeperLock.lock();

+            try {

+                zk = this.zookeeper;

+                if (zk == null || zk.getState() == null || ! zk.getState().isAlive()) {

+                    this.zookeeper = createZookeeper();

+                    recover();

+                }

+                if (zk != null) {

+                    zk.close();

+                }

+            } catch (Exception e) {

+                throw new IllegalStateException("Can not connect to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);

+            } finally {

+                zookeeperLock.unlock();

+            }

+        }

+    }

+    

+    static String appendDefaultPort(String address) {

+        if (address != null && address.length() > 0) {

+            int i = address.indexOf(':');

+            if (i < 0) {

+                return address + ":" + DEFAULT_ZOOKEEPER_PORT;

+            } else if (Integer.parseInt(address.substring(i + 1)) == 0) {

+                return address.substring(0, i + 1) + DEFAULT_ZOOKEEPER_PORT;

+            }

+        }

+        return address;

+    }

+    

+    private ZooKeeper createZookeeper() throws Exception {

+        URL url = getUrl();

+        StringBuilder address = new StringBuilder(appendDefaultPort(url.getAddress()));

+        String[] backups = url.getParameter(Constants.BACKUP_KEY, new String[0]);

+        if (backups != null && backups.length > 0) {

+            for (String backup : backups) {

+                address.append(",");

+                address.append(appendDefaultPort(backup));

+            }

+        }

+        ZooKeeper zk = new ZooKeeper(address.toString(), url.getPositiveParameter(

+                Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT), new Watcher() {

+            public void process(WatchedEvent event) {

+                try {

+                    if (event.getState() == KeeperState.Expired) {

+                        initZookeeper();

+                    } else if (event.getState() == KeeperState.SyncConnected

+                            && event.getType() == EventType.None) {

+                        recover();

+                    }

+                    if (event.getType() != EventType.NodeChildrenChanged) {

+                        return;

+                    }

+                    String path = event.getPath();

+                    if (path == null || path.length() == 0) {

+                        return;

+                    }

+                    List<String> children = watch(path);

+                    if (path.equals(toRootPath())) {

+                        List<String> services = children;

+                        if (services != null && services.size() > 0) {

+                            for (String service : services) {

+                                if (anyServices.contains(service)) {

+                                    continue;

+                                }

+                                anyServices.add(service);

+                                for (Map.Entry<String, Set<NotifyListener>> entry : anyNotifyListeners.entrySet()) {

+                                    URL subscribeUrl = URL.valueOf(entry.getKey()).setPath(service).addParameters(

+                                            Constants.INTERFACE_KEY, service, 

+                                            Constants.CHECK_KEY, String.valueOf(false), 

+                                            Constants.REGISTER_KEY, String.valueOf(false));

+                                    for (NotifyListener listener : entry.getValue()) {

+                                        subscribe(subscribeUrl, listener);

+                                    }

+                                }

+                            }

+                        }

+                    } else {

+                        String dir = toRootDir();

+                        String action = Constants.PROVIDERS;

+                        String service = path;

+                        if (service.startsWith(dir)) {

+                            service = service.substring(dir.length());

+                        }

+                        int i = service.indexOf(Constants.PATH_SEPARATOR);

+                        if (i >= 0) {

+                            action = service.substring(i + 1);

+                            service = service.substring(0, i);

+                        }

+                        service = URL.decode(service);

+                        List<String> adminChildren = null;

+                        for (Map.Entry<String, Set<NotifyListener>> entry : getSubscribed().entrySet()) {

+                            String key = entry.getKey();

+                            URL subscribe = URL.valueOf(key);

+                            List<String> notifies = children;

+                            if (subscribe.getParameter(Constants.ADMIN_KEY, false)) {

+                                if (adminChildren == null) {

+                                    adminChildren = getChildren(path.substring(0, path.lastIndexOf(Constants.PATH_SEPARATOR) + 1) + (Constants.CONSUMERS.equals(action) ? Constants.PROVIDERS : Constants.CONSUMERS));

+                                    adminChildren.addAll(children);

+                                }

+                                notifies = adminChildren;

+                            } else if (Constants.CONSUMERS.equals(action)) {

+                                continue;

+                            }

+                            String subscribeService = subscribe.getServiceInterface();

+                            if (service.equals(subscribeService)) {

+                                List<URL> list = toUrls(subscribe, notifies);

+                                if (logger.isInfoEnabled()) {

+                                    logger.info("Zookeeper service changed, service: " + service + ", urls: " + list + ", zookeeper: " + getUrl());

+                                }

+                                for (NotifyListener listener : entry.getValue()) {

+                                    ZookeeperRegistry.this.notify(subscribe, listener, list);

+                                }

+                            }

+                        }

+                    }

+                } catch (Throwable e) {

+                    logger.error("Failed to received event path " + event.getPath() + " from zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);

+                }

+            }

+        });

+        if (auth) {

+            zk.addAuthInfo(url.getUsername(), url.getPassword().getBytes());

+        }

+        return zk;

+    }

+

+    public boolean isAvailable() {

+        return zookeeper.getState().isAlive();

+    }

+

+    public void destroy() {

+        super.destroy();

+        try {

+            zookeeper.close();

+        } catch (Exception e) {

+            logger.warn("Failed to close zookeeper client " + getUrl() + ", cause: " + e.getMessage(), e);

+        }

+    }

+    

+    private boolean exists(String node) {

+        try {

+            return zookeeper.exists(node, false) != null;

+        } catch (Throwable e) {

+            return false;

+        }

+    }

+    

+    protected void doRegister(URL url) {

+        try {

+            String root = toRootPath();

+            if (root != null && root.length() > 0 && ! Constants.PATH_SEPARATOR.equals(root)

+                    && ! exists(root)) {

+                try {

+                    zookeeper.create(root, new byte[0], acl, CreateMode.PERSISTENT);

+                } catch (NodeExistsException e) {

+                }

+            }

+            String service = toServicePath(url);

+            if (! exists(service)) {

+                try {

+                    zookeeper.create(service, new byte[0], acl, CreateMode.PERSISTENT);

+                } catch (NodeExistsException e) {

+                }

+            }

+            String category = toCategoryPath(url);

+            if (! exists(category)) {

+                try {

+                    zookeeper.create(category, new byte[0], acl, CreateMode.PERSISTENT);

+                } catch (NodeExistsException e) {

+                }

+            }

+            String provider = toProviderPath(url);

+            if (exists(provider)) {

+                try {

+                    zookeeper.delete(provider, -1);

+                } catch (NoNodeException e) {

+                }

+            }

+            CreateMode createMode = Constants.ROUTE_PROTOCOL.equals(url.getProtocol()) ? CreateMode.PERSISTENT : CreateMode.EPHEMERAL;

+            try {

+                zookeeper.create(provider, new byte[0], acl, createMode);

+            } catch (NodeExistsException e) {

+                try {

+                    zookeeper.delete(provider, -1);

+                } catch (NoNodeException e2) {

+                }

+                zookeeper.create(provider, new byte[0], acl, createMode);

+            }

+        } catch (Throwable e) {

+            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);

+        }

+    }

+

+    protected void doUnregister(URL url) {

+        try {

+            String provider = toProviderPath(url);

+            zookeeper.delete(provider, -1);

+        } catch (Throwable e) {

+            throw new RpcException("Failed to unregister " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);

+        }

+    }

+

+    protected void doSubscribe(URL url, NotifyListener listener) {

+        try {

+            if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {

+                String key = url.toFullString();

+                Set<NotifyListener> listeners = anyNotifyListeners.get(key);

+                if (listeners == null) {

+                    anyNotifyListeners.putIfAbsent(key, new ConcurrentHashSet<NotifyListener>());

+                    listeners = anyNotifyListeners.get(key);

+                }

+                listeners.add(listener);

+                String root = toRootPath();

+                List<String> services = getChildren(root);

+                if (services != null && services.size() > 0) {

+                    anyServices.addAll(services);

+                    for (String service : services) {

+                        subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service, 

+                                Constants.CHECK_KEY, String.valueOf(false), Constants.REGISTER_KEY, String.valueOf(false)), listener);

+                    }

+                }

+            } else {

+                if (url.getParameter(Constants.REGISTER_KEY, true)) {

+                    register(url, null);

+                }

+                String register = toRegisterPath(url);

+                List<String> providers = getChildren(register);

+                if (url.getParameter(Constants.ADMIN_KEY, false)) {

+                    String subscribe = toSubscribePath(url);

+                    List<String> consumers = getChildren(subscribe);

+                    providers.addAll(consumers);

+                }

+                List<URL> urls = toUrls(url, providers);

+                notify(url, listener, urls);

+            }

+        } catch (Throwable e) {

+            throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);

+        }

+    }

+    

+    private List<String> getChildren(String service) throws KeeperException, InterruptedException {

+        try {

+            List<String> list = zookeeper.getChildren(service, true);

+            if (list == null || list.size() == 0) {

+                return new ArrayList<String>(0);

+            }

+            List<String> result = new ArrayList<String>();

+            for (String value : list) {

+                result.add(URL.decode(value));

+            }

+            return result;

+        } catch (KeeperException e) {

+            if (e instanceof KeeperException.NoNodeException) {

+                return new ArrayList<String>(0);

+            }

+            throw e;

+        }

+    }

+    

+    protected void doUnsubscribe(URL url, NotifyListener listener) {

+        if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {

+            String key = url.toFullString();

+            Set<NotifyListener> listeners = anyNotifyListeners.get(key);

+            if (listeners != null) {

+                listeners.remove(listener);

+            }

+        } else if (url.getParameter(Constants.REGISTER_KEY, true)) {

+            unregister(url, null);

+        }

+    }

+    

+    public List<URL> lookup(URL url) {

+        if (url == null) {

+            throw new IllegalArgumentException("lookup url == null");

+        }

+        try {

+            String register = toRegisterPath(url);

+            List<String> providers = getChildren(register);

+            if (url.getParameter(Constants.ADMIN_KEY, false)) {

+                String subscribe = toSubscribePath(url);

+                List<String> consumers = getChildren(subscribe);

+                providers.addAll(consumers);

+            }

+            return toUrls(url, providers);

+        } catch (Throwable e) {

+            throw new RpcException("Failed to lookup " + url + " from zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);

+        }

+    }

+    

+    private String toRootDir() {

+        if (root.equals(Constants.PATH_SEPARATOR)) {

+            return root;

+        }

+        return root + Constants.PATH_SEPARATOR;

+    }

+    

+    private String toRootPath() {

+        return root;

+    }

+    

+    private String toServicePath(URL url) {

+        String name = url.getServiceInterface();

+        if (Constants.ANY_VALUE.equals(name)) {

+            return toRootPath();

+        }

+        return toRootDir() + URL.encode(name);

+    }

+    

+    private String toCategoryPath(URL url) {

+        if (Constants.SUBSCRIBE_PROTOCOL.equals(url.getProtocol())) {

+            return toSubscribePath(url);

+        } else {

+            return toRegisterPath(url);

+        }

+    }

+

+    private String toRegisterPath(URL url) {

+        return toServicePath(url) + Constants.PATH_SEPARATOR + Constants.PROVIDERS;

+    }

+

+    private String toSubscribePath(URL url) {

+        return toServicePath(url) + Constants.PATH_SEPARATOR + Constants.CONSUMERS;

+    }

+

+    private String toProviderPath(URL url) {

+        return toCategoryPath(url) + Constants.PATH_SEPARATOR + URL.encode(url.toFullString());

+    }

+    

+    private List<URL> toUrls(URL consumer, List<String> providers) throws KeeperException, InterruptedException {

+        List<URL> urls = new ArrayList<URL>();

+        if (providers != null && providers.size() > 0) {

+            for (String provider : providers) {

+                provider = URL.decode(provider);

+                if (provider.contains("://")) {

+                    URL url = URL.valueOf(provider);

+                    if (UrlUtils.isMatch(consumer, url)) {

+                        urls.add(url);

+                    }

+                }

+            }

+        }

+        if (urls != null && urls.isEmpty() && consumer.getParameter(Constants.ADMIN_KEY, false)) {

+            urls.add(consumer.setProtocol(Constants.EMPTY_PROTOCOL));

+        }

+        return urls;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java
new file mode 100644
index 0000000..3151dde
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.zookeeper;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+

+/**

+ * ZookeeperRegistryFactory.

+ * 

+ * @author william.liangf

+ */

+public class ZookeeperRegistryFactory extends AbstractRegistryFactory {

+

+    public Registry createRegistry(URL url) {

+        return new ZookeeperRegistry(url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..ced9ea0
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+zookeeper=com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry-zookeeper/src/test/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryTest.java b/dubbo-registry-zookeeper/src/test/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryTest.java
new file mode 100644
index 0000000..08961d4
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/test/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryTest.java
@@ -0,0 +1,110 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.zookeeper;

+

+import junit.framework.Assert;

+

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.registry.NotifyListener;

+

+/**

+ * ZookeeperRegistryTest

+ * 

+ * @author tony.chenl

+ */

+public class ZookeeperRegistryTest {

+

+    String            service     = "com.alibaba.dubbo.test.injvmServie";

+    URL               registryUrl = URL.valueOf("zookeeper://239.255.255.255/");

+    URL               serviceUrl  = URL.valueOf("zookeeper://zookeeper/" + service

+                                                + "?notify=false&methods=test1,test2");

+    URL               consumerUrl = URL.valueOf("zookeeper://consumer/" + service + "?notify=false&methods=test1,test2");

+    // ZookeeperRegistry registry    = new ZookeeperRegistry(registryUrl);

+

+    /**

+     * @throws java.lang.Exception

+     */

+    @BeforeClass

+    public static void setUpBeforeClass() throws Exception {

+    }

+

+    /**

+     * @throws java.lang.Exception

+     */

+    @Before

+    public void setUp() throws Exception {

+        //registry.register(service, serviceUrl);

+    }

+

+    @Test(expected = IllegalStateException.class)

+    public void testUrlerror() {

+        URL errorUrl = URL.valueOf("zookeeper://zookeeper/");

+        new ZookeeperRegistry(errorUrl);

+    }

+    

+    @Test

+    public void testDefaultPort() {

+        Assert.assertEquals("10.20.153.10:2181", ZookeeperRegistry.appendDefaultPort("10.20.153.10:0"));

+        Assert.assertEquals("10.20.153.10:2181", ZookeeperRegistry.appendDefaultPort("10.20.153.10"));

+    }

+

+    /**

+     * Test method for {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#register(java.util.Map, NotifyListener)}.

+     */

+    @Test

+    public void testRegister() {

+        /*List<URL> registered = null;

+        // clear first

+        registered = registry.getRegistered(service);

+

+        for (int i = 0; i < 2; i++) {

+            registry.register(service, serviceUrl);

+            registered = registry.getRegistered(service);

+            assertTrue(registered.contains(serviceUrl));

+        }

+        // confirm only 1 regist success;

+        registered = registry.getRegistered(service);

+        assertEquals(1, registered.size());*/

+    }

+

+    /**

+     * Test method for

+     * {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#subscribe(java.util.Map, com.alibaba.dubbo.registry.support.NotifyListener)}

+     * .

+     */

+    @Test

+    public void testSubscribe() {

+        /*final String subscribearg = "arg1=1&arg2=2";

+        // verify lisener.

+        final AtomicReference<Map<String, String>> args = new AtomicReference<Map<String, String>>();

+        registry.subscribe(service, new URL("dubbo", NetUtils.getLocalHost(), 0, StringUtils.parseQueryString(subscribearg)), new NotifyListener() {

+

+            public void notify(List<URL> urls) {

+                // FIXME assertEquals(ZookeeperRegistry.this.service, service);

+                args.set(urls.get(0).getParameters());

+            }

+        });

+        assertEquals(serviceUrl.toParameterString(), StringUtils.toQueryString(args.get()));

+        Map<String, String> arg = registry.getSubscribed(service);

+        assertEquals(subscribearg, StringUtils.toQueryString(arg));*/

+

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/pom.xml b/dubbo-registry/pom.xml
new file mode 100644
index 0000000..c2ef1ab
--- /dev/null
+++ b/dubbo-registry/pom.xml
@@ -0,0 +1,49 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-registry</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Registry Module</name>
+	<description>The registry module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-cluster</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-container</artifactId>

+			<version>${project.parent.version}</version>

+			<exclusions>

+				<exclusion>

+					<groupId>org.mortbay.jetty</groupId>

+					<artifactId>jetty</artifactId>

+				</exclusion>

+			</exclusions>

+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/NotifyListener.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/NotifyListener.java
new file mode 100644
index 0000000..861abe1
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/NotifyListener.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry;
+
+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+
+/**
+ * NotifyListener. (API, Prototype, ThreadSafe)
+ * 
+ * @see com.alibaba.dubbo.registry.RegistryService#subscribe(URL, NotifyListener)
+ * @author william.liangf
+ */
+public interface NotifyListener {
+    
+    /**
+     * 当收到服务变更通知时触发
+     * @param urls 含义同{@link com.alibaba.dubbo.registry.RegistryService#register(URL, NotifyListener)}的urls参数。
+     */
+    void notify(List<URL> urls);
+    
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/Registry.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/Registry.java
new file mode 100644
index 0000000..7211b75
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/Registry.java
@@ -0,0 +1,29 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry;
+
+import com.alibaba.dubbo.common.Node;

+import com.alibaba.dubbo.common.URL;

+
+/**
+ * Registry. (SPI, Prototype, ThreadSafe)
+ * 

+ * @see com.alibaba.dubbo.registry.RegistryFactory#getRegistry(URL)
+ * @see com.alibaba.dubbo.registry.support.AbstractRegistry

+ * @author william.liangf
+ */
+public interface Registry extends Node, RegistryService {
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.java
new file mode 100644
index 0000000..64579d1
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.java
@@ -0,0 +1,42 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * RegistryFactory. (SPI, Singleton, ThreadSafe)

+ * 

+ * NOTE: RegistryFactory should <strong>NOT</strong> have default implement.

+ * 

+ * @see com.alibaba.dubbo.registry.support.AbstractRegistryFactory

+ * @author william.liangf

+ */

+@SPI("dubbo")

+public interface RegistryFactory {

+

+    /**

+     * get registry.

+     * 

+     * @param url registry url

+     * @return registry

+     */

+    @Adaptive({"protocol"})

+    Registry getRegistry(URL url);

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryService.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryService.java
new file mode 100644
index 0000000..a70beb1
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryService.java
@@ -0,0 +1,92 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * 注册中心服务

+ * 

+ * 注册中心需处理契约:<br>

+ * 1. 支持username=foo&password=bar权限认证

+ * 2. 支持timeout=1000超时设置

+ * 3. 支持backup=10.20.153.10备选注册中心地址

+ * 4. 支持file=registry.cache本地磁盘缓存

+ * 

+ * @author william.liangf

+ */

+public interface RegistryService {

+

+    /**

+     * 注册服务.

+     * 

+     * 注册需处理契约:<br>

+     * 1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试。<br>

+     * 2. 允许写入route://协议的路由规则,且持久存储路由规则。<br>

+     * 3. 允许URI相同但参数不同的URL并存,不能覆盖。<br>

+     * 4. 当服务提供者出现断电等情况异常退出时,需自动删除当前提供者URL。<br>

+     * 5. 当注册中心重启恢复时,需自动恢复注册数据。<br>

+     * 

+     * @param url 服务提供者地址,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin

+     * @param listener 服务变更事件监听器

+     */

+    void register(URL url, NotifyListener listener);

+

+    /**

+     * 取消注册服务

+     * 

+     * @param url 服务提供者地址,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin

+     * @param  listener 服务变更事件监听器

+     */

+    void unregister(URL url, NotifyListener listener);

+

+    /**

+     * 订阅服务

+     * 

+     * 订阅需处理契约:<br>

+     * 1. 当URL设置了check=false时,订阅失败后不报错,在后台定时重试<br>

+     * 2. 当URL设置了register=false时,不记录订阅者的URL<br>

+     * 3. 当URL设置了admin=true时,结果不只包含服务提供者的URL和路由规则URL,还需包含所有服务订阅者的URL<br>

+     * 4. 允许以interface,group,version,classifier作为条件查询,如:interface=com.alibaba.foo.BarService&group=foo&version=1.0.0&classifier=william<br>

+     * 5. 允许星号通配,订阅所有接口的所有分组的所有版本,如:interface=*&group=*&version=*&classifier=* <br>

+     * 6. 允许URI相同但参数不同的URL并存,不能覆盖。<br>

+     * 7. 当服务消费者出现断电等情况异常退出时,需自动删除当前消费者URL。<br>

+     * 8. 当注册中心重启恢复时,需自动恢复订阅请求。<br>

+     * 

+     * @param url 服务查询键值对,如:subscribe://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin

+     * @param listener 服务变更事件监听器

+     */

+    void subscribe(URL url, NotifyListener listener);

+

+    /**

+     * 取消订阅服务

+     * 

+     * @param url 服务查询键值对,如:subscribe://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin

+     * @param listener 服务变更事件监听器

+     */

+    void unsubscribe(URL url, NotifyListener listener);

+    

+    /**

+     * 查询服务列表,与订阅服务相同,拉模式,只返回一次结果。

+     * 

+     * @param url 服务查询键值对,如:subscribe://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin

+     * @return 服务列表

+     */

+    List<URL> lookup(URL url);

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/RegisteredPageHandler.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/RegisteredPageHandler.java
new file mode 100644
index 0000000..7217921
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/RegisteredPageHandler.java
@@ -0,0 +1,79 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.pages;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.support.AbstractRegistry;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+

+/**

+ * RegisteredPageHandler

+ * 

+ * @author william.liangf

+ */

+public class RegisteredPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        String registryAddress = url.getParameter("registry", "");

+        List<List<String>> rows = new ArrayList<List<String>>();

+        Collection<Registry> registries = AbstractRegistryFactory.getRegistries();

+        StringBuilder select = new StringBuilder();

+        Registry registry = null;

+        if (registries != null && registries.size() > 0) {

+            if (registries.size() == 1) {

+                registry = registries.iterator().next();

+                select.append(" &gt; " + registry.getUrl().getAddress());

+            } else {

+                select.append(" &gt; <select onchange=\"window.location.href='registered.html?registry=' + this.value;\">");

+                for (Registry r : registries) {

+                    String sp = r.getUrl().getAddress();

+                    select.append("<option value=\">");

+                    select.append(sp);

+                    if (((registryAddress == null || registryAddress.length() == 0) && registry == null)

+                            || registryAddress.equals(sp)) {

+                        registry = r;

+                        select.append("\" selected=\"selected");

+                    }

+                    select.append("\">");

+                    select.append(sp);

+                    select.append("</option>");

+                }

+                select.append("</select>");

+            }

+        }

+        if (registry instanceof AbstractRegistry) {

+            Set<String> services = ((AbstractRegistry) registry).getRegistered();

+            if (services != null && services.size() > 0) {

+                for (String u : services) {

+                    List<String> row = new ArrayList<String>();

+                    row.add(u.replace("<", "&lt;").replace(">", "&gt;"));

+                    rows.add(row);

+                }

+            }

+        }

+        return new Page("<a href=\"registries.html\">Registries</a>" + select.toString() + " &gt; Registered | <a href=\"subscribed.html?registry=" + registryAddress + "\">Subscribed</a>", "Registered (" + rows.size() + ")",

+                new String[] { "Provider URL:" }, rows);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/RegistriesPageHandler.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/RegistriesPageHandler.java
new file mode 100644
index 0000000..bfe06ff
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/RegistriesPageHandler.java
@@ -0,0 +1,71 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.pages;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.container.page.Menu;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.support.AbstractRegistry;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+

+/**

+ * RegistriesPageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "Registries", desc = "Show connected registries.", order = 10000)

+public class RegistriesPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        List<List<String>> rows = new ArrayList<List<String>>();

+        Collection<Registry> registries = AbstractRegistryFactory.getRegistries();

+        int registeredCount = 0;

+        int subscribedCount = 0;

+        if (registries != null && registries.size() > 0) {

+            for (Registry registry : registries) {

+                String server = registry.getUrl().getAddress();

+                List<String> row = new ArrayList<String>();

+                row.add(NetUtils.getHostName(server) + "/" + server);

+                if (registry.isAvailable()) {

+                    row.add("<font color=\"green\">Connected</font>");

+                } else {

+                    row.add("<font color=\"red\">Disconnected</font>");

+                }

+                int registeredSize = 0;

+                int subscribedSize = 0;

+                if (registry instanceof AbstractRegistry) {

+                    registeredSize = ((AbstractRegistry) registry).getRegistered().size();

+                    registeredCount += registeredSize;

+                    subscribedSize = ((AbstractRegistry) registry).getSubscribed().size();

+                    subscribedCount += subscribedSize;

+                }

+                row.add("<a href=\"registered.html?registry=" + server + "\">Registered(" + registeredSize + ")</a>");

+                row.add("<a href=\"subscribed.html?registry=" + server + "\">Subscribed(" + subscribedSize + ")</a>");

+                rows.add(row);

+            }

+        }

+        return new Page("Registries", "Registries (" + rows.size() + ")",

+                new String[] { "Registry Address:", "Status", "Registered(" + registeredCount + ")", "Subscribed(" + subscribedCount + ")" }, rows);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/SubscribedPageHandler.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/SubscribedPageHandler.java
new file mode 100644
index 0000000..ef58259
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/SubscribedPageHandler.java
@@ -0,0 +1,79 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.pages;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.support.AbstractRegistry;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+

+/**

+ * SubscribedPageHandler

+ * 

+ * @author william.liangf

+ */

+public class SubscribedPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        String registryAddress = url.getParameter("registry", "");

+        List<List<String>> rows = new ArrayList<List<String>>();

+        Collection<Registry> registries = AbstractRegistryFactory.getRegistries();

+        StringBuilder select = new StringBuilder();

+        Registry registry = null;

+        if (registries != null && registries.size() > 0) {

+            if (registries.size() == 1) {

+                registry = registries.iterator().next();

+                select.append(" &gt; " + registry.getUrl().getAddress());

+            } else {

+                select.append(" &gt; <select onchange=\"window.location.href='subscribed.html?registry=' + this.value;\">");

+                for (Registry r : registries) {

+                    String sp = r.getUrl().getAddress();

+                    select.append("<option value=\">");

+                    select.append(sp);

+                    if (((registryAddress == null || registryAddress.length() == 0) && registry == null)

+                            || registryAddress.equals(sp)) {

+                        registry = r;

+                        select.append("\" selected=\"selected");

+                    }

+                    select.append("\">");

+                    select.append(sp);

+                    select.append("</option>");

+                }

+                select.append("</select>");

+            }

+        }

+        if (registry instanceof AbstractRegistry) {

+            Set<String> services = ((AbstractRegistry) registry).getSubscribed().keySet();

+            if (services != null && services.size() > 0) {

+                for (String u : services) {

+                    List<String> row = new ArrayList<String>();

+                    row.add(u.replace("<", "&lt;").replace(">", "&gt;"));

+                    rows.add(row);

+                }

+            }

+        }

+        return new Page("<a href=\"registries.html\">Registries</a>" + select.toString() + " &gt; <a href=\"registered.html?registry=" + registryAddress + "\">Registered</a> | Subscribed", "Subscribed (" + rows.size() + ")",

+                new String[] { "Consumer URL:" }, rows);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.java
new file mode 100644
index 0000000..977b317
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.java
@@ -0,0 +1,185 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.support;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.common.utils.UrlUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.Registry;

+

+/**

+ * 嵌入式注册中心实现,不开端口,只是map进行存储查询.不需要显示声明

+ * 

+ * @author chao.liuc

+ * @author william.liangf

+ */

+public abstract class AbstractRegistry implements Registry {

+

+    // 日志输出

+    protected final Logger logger = LoggerFactory.getLogger(getClass());

+

+    private URL registryUrl;

+

+    private final Set<String> registered = new ConcurrentHashSet<String>();

+

+    private final ConcurrentMap<String, Set<NotifyListener>> subscribed = new ConcurrentHashMap<String, Set<NotifyListener>>();

+

+    public AbstractRegistry(URL url) {

+        setUrl(url);

+    }

+    

+    protected void setUrl(URL url) {

+        if (url == null) {

+            throw new IllegalArgumentException("registry url == null");

+        }

+        this.registryUrl = url;

+    }

+

+    public Set<String> getRegistered() {

+        return registered;

+    }

+

+    public Map<String, Set<NotifyListener>> getSubscribed() {

+        return subscribed;

+    }

+

+    public URL getUrl() {

+        return registryUrl;

+    }

+

+    public List<URL> lookup(URL url) {

+        List<URL> urls= new ArrayList<URL>();

+        for (String r: getRegistered()) {

+            URL u = URL.valueOf(r);

+            if (UrlUtils.isMatch(url, u)) {

+                urls.add(u);

+            }

+        }

+        return urls;

+    }

+

+    public void register(URL url, NotifyListener listener) {

+        if (url == null) {

+            throw new IllegalArgumentException("register url == null");

+        }

+        if (logger.isInfoEnabled()){

+            logger.info("Register: " + url);

+        }

+        registered.add(url.toFullString());

+    }

+    

+    public void unregister(URL url, NotifyListener listener) {

+        if (url == null) {

+            throw new IllegalArgumentException("unregister url == null");

+        }

+        if (logger.isInfoEnabled()){

+            logger.info("Unregister: " + url);

+        }

+        registered.remove(url.toFullString());

+    }

+    

+    public void subscribe(URL url, NotifyListener listener) {

+        if (url == null) {

+            throw new IllegalArgumentException("subscribe url == null");

+        }

+        if (logger.isInfoEnabled()){

+            logger.info("Subscribe: " + url);

+        }

+        if (listener == null) {

+            throw new IllegalArgumentException("subscribe listener == null");

+        }

+        String key = url.toFullString();

+        Set<NotifyListener> listeners = subscribed.get(key);

+        if (listeners == null) {

+            subscribed.putIfAbsent(key, new ConcurrentHashSet<NotifyListener>());

+            listeners = subscribed.get(key);

+        }

+        listeners.add(listener);

+    }

+    

+    public void unsubscribe(URL url, NotifyListener listener) {

+        if (url == null) {

+            throw new IllegalArgumentException("unsubscribe url == null");

+        }

+        if (listener == null) {

+            throw new IllegalArgumentException("unsubscribe listener == null");

+        }

+        if (logger.isInfoEnabled()){

+            logger.info("Unsubscribe: " + url);

+        }

+        String key = url.toFullString();

+        Set<NotifyListener> listeners = subscribed.get(key);

+        if (listeners != null) {

+            listeners.remove(listener);

+        }

+    }

+

+    protected void recover() throws Exception {

+        // register

+        Set<String> recoverRegistered = new HashSet<String>(getRegistered());

+        if (! recoverRegistered.isEmpty()) {

+            if (logger.isInfoEnabled()) {

+                logger.info("Recover register services " + recoverRegistered);

+            }

+            for (String url : recoverRegistered) {

+                register(URL.valueOf(url), null);

+            }

+        }

+        // subscribe

+        Map<String, Set<NotifyListener>> recoverSubscribed = new HashMap<String, Set<NotifyListener>>(getSubscribed());

+        if (recoverSubscribed.size() > 0) {

+            if (logger.isInfoEnabled()) {

+                logger.info("Recover subscribe services " + recoverSubscribed);

+            }

+            for (Map.Entry<String, Set<NotifyListener>> entry : recoverSubscribed.entrySet()) {

+                String url = entry.getKey();

+                for (NotifyListener listener : entry.getValue()) {

+                    subscribe(URL.valueOf(url), listener);

+                }

+            }

+        }

+    }

+    

+    public void destroy() {

+        if (logger.isInfoEnabled()){

+            logger.info("Destroy registry: " + getUrl());

+        }

+        for (String url : new HashSet<String>(registered)) {

+            try {

+                unregister(URL.valueOf(url), null);

+            } catch (Throwable t) {

+                logger.warn(t.getMessage(), t);

+            }

+        }

+    }

+    

+    public String toString() {

+        return getUrl().toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.java
new file mode 100644
index 0000000..f95c765
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.java
@@ -0,0 +1,106 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.support;

+

+import java.util.Collection;

+import java.util.Collections;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.locks.ReentrantLock;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.RegistryFactory;

+

+/**

+ * RegistryLocators. (API, Static, ThreadSafe)

+ * 

+ * @see com.alibaba.dubbo.registry.RegistryFactory

+ * @author william.liangf

+ */

+public abstract class AbstractRegistryFactory implements RegistryFactory {

+

+    // 日志输出

+    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRegistryFactory.class);

+

+    // 注册中心获取过程锁

+    protected static final ReentrantLock LOCK = new ReentrantLock();

+    

+    // 注册中心集合 Map<RegistryAddress, Registry>

+    protected static final Map<String, Registry> REGISTRIES = new ConcurrentHashMap<String, Registry>();

+

+    public Registry getRegistry(URL url) {

+        // 锁定注册中心获取过程,保证注册中心单一实例

+        LOCK.lock();

+        try {

+            Registry registry = REGISTRIES.get(getCacheKey(url));

+            if (registry != null) {

+                return registry;

+            }

+            registry = createRegistry(url);

+            if (registry == null) {

+                throw new IllegalStateException("Can not create registry " + url);

+            }

+            REGISTRIES.put(getCacheKey(url), registry);

+            return registry;

+        } finally {

+            // 释放锁

+            LOCK.unlock();

+        }

+    }

+    

+    protected abstract Registry createRegistry(URL url);

+

+    /**

+     * 获取所有注册中心

+     * 

+     * @return 所有注册中心

+     */

+    public static Collection<Registry> getRegistries() {

+        return Collections.unmodifiableCollection(REGISTRIES.values());

+    }

+    

+    /**

+     * 关闭所有已创建注册中心

+     */

+    public static void destroyAll() {

+        if (LOGGER.isInfoEnabled()) {

+            LOGGER.info("Close all registries " + getRegistries());

+        }

+        // 锁定注册中心关闭过程

+        LOCK.lock();

+        try {

+            for (Registry registry : getRegistries()) {

+                try {

+                    registry.destroy();

+                } catch (Throwable e) {

+                    LOGGER.error(e.getMessage(), e);

+                }

+            }

+            REGISTRIES.clear();

+        } finally {

+            // 释放锁

+            LOCK.unlock();

+        }

+    }

+    

+    protected static String getCacheKey(URL url){

+        return url.getProtocol() + "://" + url.getUsername() + ":" + url.getPassword() + "@" + url.getIp() + ":" + url.getPort();

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryService.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryService.java
new file mode 100644
index 0000000..1d53918
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryService.java
@@ -0,0 +1,242 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.support;

+

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.CopyOnWriteArrayList;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.RegistryService;

+

+/**

+ * AbstractRegistryService

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractRegistryService implements RegistryService {

+

+    // 日志输出

+    protected final Logger logger = LoggerFactory.getLogger(getClass());

+

+    // 已注册的服务

+    // Map<serviceName, Map<url, queryString>>

+    private final ConcurrentMap<String, List<URL>> registered = new ConcurrentHashMap<String, List<URL>>();

+

+    // 已订阅的服务

+    // Map<serviceName, queryString>

+    private final ConcurrentMap<String, Map<String, String>> subscribed = new ConcurrentHashMap<String, Map<String, String>>();

+

+    // 已通知的服务

+    // Map<serviceName, Map<url, queryString>>

+    private final ConcurrentMap<String, List<URL>> notified = new ConcurrentHashMap<String, List<URL>>();

+    

+    // 已订阅服务的监听器列表

+    // Map<serviceName, List<notificationListener>>

+    private final ConcurrentMap<String, List<NotifyListener>> notifyListeners = new ConcurrentHashMap<String, List<NotifyListener>>();

+    

+    public void register(URL url, NotifyListener listener) {

+        if (logger.isInfoEnabled()) {

+            logger.info("Register service: " + url.getServiceKey() + ",url:" + url);

+        }

+        register(url.getServiceKey(), url, listener);

+    }

+

+    public void unregister(URL url, NotifyListener listener) {

+        if (logger.isInfoEnabled()) {

+            logger.info("Unregister service: " + url.getServiceKey() + ",url:" + url);

+        }

+        unregister(url.getServiceKey(), url, listener);

+    }

+

+    public void subscribe(URL url, NotifyListener listener) {

+        if (logger.isInfoEnabled()) {

+            logger.info("Subscribe service: " + url.getServiceKey() + ",url:" + url);

+        }

+        subscribe(url.getServiceKey(), url, listener);

+    }

+    

+    public void unsubscribe(URL url, NotifyListener listener) {

+        if (logger.isInfoEnabled()) {

+            logger.info("Unsubscribe service: " + url.getServiceKey() + ",url:" + url);

+        }

+        unsubscribe(url.getServiceKey(), url, listener);

+    }

+

+    public List<URL> lookup(URL url) {

+        return getRegistered(url.getServiceKey());

+    }

+

+    public void register(String service, URL url, NotifyListener listener) {

+        if (service == null) {

+            throw new IllegalArgumentException("service == null");

+        }

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        List<URL> urls = registered.get(service);

+        if (urls == null) {

+            registered.putIfAbsent(service, new CopyOnWriteArrayList<URL>());

+            urls = registered.get(service);

+        }

+        if (! urls.contains(url)) {

+            urls.add(url);

+        }

+        addListener(service, listener);

+    }

+    

+    public void unregister(String service, URL url, NotifyListener listener) {

+        if (service == null) {

+            throw new IllegalArgumentException("service == null");

+        }

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        List<URL> urls = registered.get(service);

+        if (urls != null) {

+            URL deleteURL = null;

+            for (URL u : urls) {

+                if (u.toIdentityString().equals(url.toIdentityString())) {

+                    deleteURL = u;

+                    break;

+                }

+            }

+            if (deleteURL != null) {

+                urls.remove(deleteURL);

+            }

+        }

+        removeListener(service, listener);

+    }

+    

+    public void subscribe(String service, URL url, NotifyListener listener) {

+        if (service == null) {

+            throw new IllegalArgumentException("service == null");

+        }

+        if (url == null) {

+            throw new IllegalArgumentException("parameters == null");

+        }

+        if (listener == null) {

+            throw new IllegalArgumentException("listener == null");

+        }

+        subscribed.put(service, url.getParameters()); 

+        addListener(service, listener);

+    }

+

+    public void unsubscribe(String service, URL url, NotifyListener listener) {

+        if (service == null) {

+            throw new IllegalArgumentException("service == null");

+        }

+        if (url == null) {

+            throw new IllegalArgumentException("parameters == null");

+        }

+        if (listener == null) {

+            throw new IllegalArgumentException("listener == null");

+        }

+        subscribed.remove(service);

+        removeListener(service, listener);

+    }

+    

+    //consumer 与 provider的 listener可以一起存储,都是根据服务名称共享

+    private void addListener(final String service, final NotifyListener listener){

+        if (listener == null) {

+            return;

+        }

+        List<NotifyListener> listeners = notifyListeners.get(service);

+        if (listeners == null) {

+            notifyListeners.putIfAbsent(service, new CopyOnWriteArrayList<NotifyListener>());

+            listeners = notifyListeners.get(service);

+        }

+        if (listeners != null && !listeners.contains(listener)){

+            listeners.add(listener);

+        }

+    }

+    

+    private void removeListener(final String service, final NotifyListener listener){

+        if (listener == null) {

+            return;

+        }

+        List<NotifyListener> listeners = notifyListeners.get(service);

+        if (listeners != null) {

+            listeners.remove(listener);

+        }

+    }

+    

+    private void doNotify(String service, List<URL> urls) {

+        notified.put(service, urls);

+        List<NotifyListener> listeners = notifyListeners.get(service);

+        if (listeners != null) {

+            for (NotifyListener listener : listeners) {

+                try {

+                    notify(service, urls, listener);

+                } catch (Throwable t) {

+                    logger.error("Failed to notify registry event, service: " + service + ", urls: " +  urls + ", cause: " + t.getMessage(), t);

+                }

+            }

+        }

+    }

+    

+    protected void notify(String service, List<URL> urls, NotifyListener listener) {

+        listener.notify(urls);

+    }

+    

+    protected final void forbid(String service) {

+        doNotify(service, new ArrayList<URL>(0));

+    }

+

+    protected final void notify(String service, List<URL> urls) {

+        if (service == null || service.length() == 0

+                || urls == null || urls.size() == 0) {

+            return;

+        }

+        doNotify(service, urls);

+    }

+    

+    public Map<String, List<URL>> getRegistered() {

+        return Collections.unmodifiableMap(registered);

+    }

+    

+    public List<URL> getRegistered(String service) {

+        return Collections.unmodifiableList(registered.get(service));

+    }

+    

+    public Map<String, Map<String, String>> getSubscribed() {

+        return Collections.unmodifiableMap(subscribed);

+    }

+    

+    public Map<String, String> getSubscribed(String service) {

+        return subscribed.get(service);

+    }

+    

+    public Map<String, List<URL>> getNotified() {

+        return Collections.unmodifiableMap(notified);

+    }

+    

+    public List<URL> getNotified(String service) {

+        return Collections.unmodifiableList(notified.get(service));

+    }

+    

+    public Map<String, List<NotifyListener>> getListeners() {

+        return Collections.unmodifiableMap(notifyListeners);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
new file mode 100644
index 0000000..ff22740
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
@@ -0,0 +1,369 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.support;

+

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.registry.NotifyListener;

+

+/**

+ * FailbackRegistry

+ * 

+ * @author william.liangf

+ */

+public abstract class FailbackRegistry extends AbstractRegistry {

+

+    // 定时任务执行器

+    private final ScheduledExecutorService retryExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryFailedRetryTimer", true));

+

+    // 失败重试定时器,定时检查是否有请求失败,如有,无限次重试

+    private final ScheduledFuture<?> retryFuture;

+    

+    private final Set<String> failedRegistered = new ConcurrentHashSet<String>();

+

+    private final Set<String> failedUnregistered = new ConcurrentHashSet<String>();

+    

+    private final ConcurrentMap<String, Set<NotifyListener>> failedSubscribed = new ConcurrentHashMap<String, Set<NotifyListener>>();

+    

+    private final ConcurrentMap<String, Set<NotifyListener>> failedUnsubscribed = new ConcurrentHashMap<String, Set<NotifyListener>>();

+

+    private final ConcurrentMap<String, Map<NotifyListener, List<URL>>> failedNotified = new ConcurrentHashMap<String, Map<NotifyListener, List<URL>>>();

+    

+    public FailbackRegistry(URL url) {

+        super(url);

+        int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);

+        this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {

+            public void run() {

+                // 检测并连接注册中心

+                try {

+                    retry();

+                } catch (Throwable t) { // 防御性容错

+                    logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);

+                }

+            }

+        }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);

+    }

+    

+    // 重试失败的动作

+    private void retry() throws Exception {

+        if (! failedRegistered.isEmpty()) {

+            Set<String> failed = new HashSet<String>(failedRegistered);

+            if (failed.size() > 0) {

+                if (logger.isInfoEnabled()) {

+                    logger.info("Retry register " + failed);

+                }

+                try {

+                    for (String url : failed) {

+                        try {

+                            doRegister(URL.valueOf(url));

+                            failedRegistered.remove(url);

+                        } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                            logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                        }

+                    }

+                } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                    logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                }

+            }

+        }

+        if(! failedUnregistered.isEmpty()) {

+            Set<String> failed = new HashSet<String>(failedUnregistered);

+            if (failed.size() > 0) {

+                if (logger.isInfoEnabled()) {

+                    logger.info("Retry unregister " + failed);

+                }

+                try {

+                    for (String url : failed) {

+                        try {

+                            doUnregister(URL.valueOf(url));

+                            failedUnregistered.remove(url);

+                        } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                            logger.warn("Failed to retry unregister  " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                        }

+                    }

+                } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                    logger.warn("Failed to retry unregister  " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                }

+            }

+        }

+        if (! failedSubscribed.isEmpty()) {

+            Map<String, Set<NotifyListener>> failed = new HashMap<String, Set<NotifyListener>>(failedSubscribed);

+            for (Map.Entry<String, Set<NotifyListener>> entry : failed.entrySet()) {

+                if (entry.getValue() == null || entry.getValue().size() == 0) {

+                    failed.remove(entry.getKey());

+                }

+            }

+            if (failed.size() > 0) {

+                if (logger.isInfoEnabled()) {

+                    logger.info("Retry subscribe " + failed);

+                }

+                try {

+                    for (Map.Entry<String, Set<NotifyListener>> entry : failed.entrySet()) {

+                        URL url = URL.valueOf(entry.getKey());

+                        Set<NotifyListener> listeners = entry.getValue();

+                        for (NotifyListener listener : listeners) {

+                            try {

+                                doSubscribe(url, listener);

+                                listeners.remove(listener);

+                            } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                                logger.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                            }

+                        }

+                    }

+                } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                    logger.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                }

+            }

+        }

+        if (! failedUnsubscribed.isEmpty()) {

+            Map<String, Set<NotifyListener>> failed = new HashMap<String, Set<NotifyListener>>(failedUnsubscribed);

+            for (Map.Entry<String, Set<NotifyListener>> entry : failed.entrySet()) {

+                if (entry.getValue() == null || entry.getValue().size() == 0) {

+                    failed.remove(entry.getKey());

+                }

+            }

+            if (failed.size() > 0) {

+                if (logger.isInfoEnabled()) {

+                    logger.info("Retry unsubscribe " + failed);

+                }

+                try {

+                    for (Map.Entry<String, Set<NotifyListener>> entry : failed.entrySet()) {

+                        URL url = URL.valueOf(entry.getKey());

+                        Set<NotifyListener> listeners = entry.getValue();

+                        for (NotifyListener listener : listeners) {

+                            try {

+                                doUnsubscribe(url, listener);

+                                listeners.remove(listener);

+                            } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                                logger.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                            }

+                        }

+                    }

+                } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                    logger.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                }

+            }

+        }

+        if (! failedNotified.isEmpty()) {

+            Map<String, Map<NotifyListener, List<URL>>> failed = new HashMap<String, Map<NotifyListener, List<URL>>>(failedNotified);

+            for (Map.Entry<String, Map<NotifyListener, List<URL>>> entry : failed.entrySet()) {

+                if (entry.getValue() == null || entry.getValue().size() == 0) {

+                    failed.remove(entry.getKey());

+                }

+            }

+            if (failed.size() > 0) {

+                if (logger.isInfoEnabled()) {

+                    logger.info("Retry notify " + failed);

+                }

+                try {

+                    for (Map<NotifyListener, List<URL>> values : failed.values()) {

+                        for (Map.Entry<NotifyListener, List<URL>> entry : values.entrySet()) {

+                            try {

+                                NotifyListener listener = entry.getKey();

+                                List<URL> urls = entry.getValue();

+                                listener.notify(urls);

+                                values.remove(listener);

+                            } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                                logger.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                            }

+                        }

+                    }

+                } catch (Throwable t) { // 忽略所有异常,等待下次重试

+                    logger.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);

+                }

+            }

+        }

+        doRetry();

+    }

+    

+    public void destroy() {

+        super.destroy();

+        try {

+            retryFuture.cancel(true);

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+    }

+    

+    public void register(URL url, NotifyListener listener) {

+        super.register(url, listener);

+        try {

+            // 向服务器端发送注册请求

+            doRegister(url);

+            removeFailedRegistered(url);

+        } catch (Exception t) {

+            if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常

+                throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);

+            }

+            // 否则,将失败的注册请求记录到失败列表,定时重试

+            failedRegistered.add(url.toFullString());

+            logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);

+        }

+    }

+

+    public void unregister(URL url, NotifyListener listener) {

+        super.unregister(url, listener);

+        try {

+            // 向服务器端发送取消注册请求

+            doUnregister(url);

+            removeFailedRegistered(url);

+        } catch (Exception t) {

+            if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常

+                throw new IllegalStateException("Failed to unregister " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);

+            }

+            // 否则,将失败的取消注册请求记录到失败列表,定时重试

+            failedUnregistered.add(url.toFullString());

+            logger.error("Failed to uregister " + url + ", waiting for retry, cause: " + t.getMessage(), t);

+        }

+    }

+

+    private void removeFailedRegistered(URL url) {

+        String key = url.toFullString();

+        failedRegistered.remove(key);

+        failedUnregistered.remove(key);

+    }

+

+    private void removeFailedSubscribed(URL url, NotifyListener listener) {

+        String key = url.toFullString();

+        Set<NotifyListener> listeners = failedSubscribed.get(key);

+        if (listeners != null) {

+            listeners.remove(listener);

+        }

+        listeners = failedUnsubscribed.get(key);

+        if (listeners != null) {

+            listeners.remove(listener);

+        }

+        Map<NotifyListener, List<URL>> notified = failedNotified.get(key);

+        if (notified != null) {

+            notified.remove(listener);

+        }

+    }

+

+    public void subscribe(URL url, NotifyListener listener) {

+        super.subscribe(url, listener);

+        try {

+            // 向服务器端发送订阅请求

+            doSubscribe(url, listener);

+            removeFailedSubscribed(url, listener);

+        } catch (Exception t) {

+            if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常

+                throw new IllegalStateException("Failed to subscribe " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);

+            }

+            // 否则,将失败的订阅请求记录到失败列表,定时重试

+            String key = url.toFullString();

+            Set<NotifyListener> listeners = failedSubscribed.get(key);

+            if (listeners == null) {

+                failedSubscribed.putIfAbsent(key, new ConcurrentHashSet<NotifyListener>());

+                listeners = failedSubscribed.get(key);

+            }

+            listeners.add(listener);

+            logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);

+        }

+    }

+

+    public void unsubscribe(URL url, NotifyListener listener) {

+        super.unsubscribe(url, listener);

+        try {

+            // 向服务器端发送取消订阅请求

+            doUnsubscribe(url, listener);

+            removeFailedSubscribed(url, listener);

+        } catch (Exception t) {

+            if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常

+                throw new IllegalStateException("Failed to unsubscribe " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);

+            }

+            // 否则,将失败的取消订阅请求记录到失败列表,定时重试

+            String key = url.toFullString();

+            Set<NotifyListener> listeners = failedUnsubscribed.get(key);

+            if (listeners == null) {

+                failedUnsubscribed.putIfAbsent(key, new ConcurrentHashSet<NotifyListener>());

+                listeners = failedUnsubscribed.get(key);

+            }

+            listeners.add(listener);

+            logger.error("Failed to unsubscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);

+        }

+    }

+    

+    protected void notify(URL url, NotifyListener listener, List<URL> urls) {

+        if (url == null) {

+            throw new IllegalArgumentException("notify url == null");

+        }

+        if (listener == null) {

+            throw new IllegalArgumentException("notify listener == null");

+        }

+        if ((urls == null || urls.size() == 0) 

+                && ! url.getParameter(Constants.ADMIN_KEY, false)) {

+            return;

+        }

+        try {

+            listener.notify(urls);

+        } catch (Exception t) {

+            // 将失败的通知请求记录到失败列表,定时重试

+            String key = url.toFullString();

+            Map<NotifyListener, List<URL>> values = failedNotified.get(key);

+            if (values == null) {

+                failedNotified.putIfAbsent(key, new ConcurrentHashMap<NotifyListener, List<URL>>());

+                values = failedNotified.get(key);

+            }

+            values.put(listener, urls);

+            logger.error("Failed to unsubscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);

+        }

+    }

+

+    protected abstract void doRegister(URL url);

+    

+    protected abstract void doUnregister(URL url);

+    

+    protected abstract void doSubscribe(URL url, NotifyListener listener);

+    

+    protected abstract void doUnsubscribe(URL url, NotifyListener listener);

+

+    protected void doRetry() {}

+

+    public Set<String> getFailedRegistered() {

+        return failedRegistered;

+    }

+

+    public Set<String> getFailedUnregistered() {

+        return failedUnregistered;

+    }

+

+    public Map<String, Set<NotifyListener>> getFailedSubscribed() {

+        return failedSubscribed;

+    }

+

+    public Map<String, Set<NotifyListener>> getFailedUnsubscribed() {

+        return failedUnsubscribed;

+    }

+

+    public Map<String, Map<NotifyListener, List<URL>>> getFailedNotified() {

+        return failedNotified;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryDirectory.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryDirectory.java
new file mode 100644
index 0000000..7f0dd4d
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryDirectory.java
@@ -0,0 +1,624 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.support;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.Comparator;

+import java.util.HashMap;

+import java.util.HashSet;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.RegistryService;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.cluster.Router;

+import com.alibaba.dubbo.rpc.cluster.RouterFactory;

+import com.alibaba.dubbo.rpc.cluster.directory.AbstractDirectory;

+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;

+import com.alibaba.dubbo.rpc.cluster.router.ScriptRouterFactory;

+import com.alibaba.dubbo.rpc.cluster.support.ClusterUtils;

+import com.alibaba.dubbo.rpc.protocol.InvokerWrapper;

+

+/**

+ * RegistryDirectory

+ * 

+ * @author william.liangf

+ * @author chao.liuc

+ */

+public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {

+

+    private static final Logger logger = LoggerFactory.getLogger(RegistryDirectory.class);

+    

+    private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();

+    

+    private volatile boolean forbidden = false;

+    

+    private final String serviceKey;

+

+    private final Class<T> serviceType;

+    

+    private final boolean multiGroup;

+

+    private volatile URL directoryUrl;

+    

+    private volatile URL overrideDirectoryUrl;

+

+    private volatile Map<String, String> queryMap;

+    

+    /*override规则 

+     * 优先级:override>-D>consumer>provider

+     * 第一种规则:针对某个provider <ip:port,timeout=100>

+     * 第二种规则:针对所有provider <* ,timeout=5000>

+     */

+    private volatile Map<String, Map<String, String>> overrideMap;

+    

+    // Map<url, Invoker> cache service url to invoker mapping.

+    private Map<String, Invoker<T>> urlInvokerMap = new ConcurrentHashMap<String, Invoker<T>>();

+    

+    // Map<methodName, Invoker> cache service method to invokers mapping.

+    private volatile Map<String, List<Invoker<T>>> methodInvokerMap;

+

+    private volatile Protocol protocol;

+

+    private volatile Registry registry;

+    

+    public RegistryDirectory(Class<T> serviceType, URL url) {

+        super(url);

+        if(serviceType == null )

+            throw new IllegalArgumentException("service type is null.");

+        if(url.getServiceKey() == null || url.getServiceKey().length() == 0)

+            throw new IllegalArgumentException("registry serviceKey is null.");

+        this.serviceType = serviceType;

+        this.serviceKey = url.getServiceKey();

+        this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));

+        this.overrideDirectoryUrl = this.directoryUrl = url.removeParameters(Constants.REFER_KEY, Constants.EXPORT_KEY)

+                .addParameters(queryMap).removeParameter(Constants.MONITOR_KEY);

+        String group = directoryUrl.getParameter( Constants.GROUP_KEY, "" );

+        this.multiGroup = group != null && ("*".equals(group) || group.contains( "," ));

+    }

+

+    public void setProtocol(Protocol protocol) {

+        this.protocol = protocol;

+    }

+

+    public void setRegistry(Registry registry) {

+        this.registry = registry;

+    }

+

+    public void destroy() {

+        if(destroyed) {

+            return;

+        }

+        // unsubscribe.

+        try {

+            if(registry != null && registry.isAvailable()) {

+                registry.unsubscribe(new URL(Constants.SUBSCRIBE_PROTOCOL, NetUtils.getLocalHost(), 0, RegistryService.class.getName(), getUrl().getParameters()), this);

+            }

+        } catch (Throwable t) {

+            logger.warn("unexpeced error when unsubscribe service " + serviceKey + "from registry" + registry.getUrl(), t);

+        }

+        super.destroy(); // 必须在unsubscribe之后执行

+        try {

+            destroyAllInvokers();

+        } catch (Throwable t) {

+            logger.warn("Failed to destroy service " + serviceKey, t);

+        }

+    }

+

+    public synchronized void notify(List<URL> urls) {

+        if (urls == null || urls.size() == 0) { // 黑白名单限制

+            this.forbidden = true; // 禁止访问

+            this.methodInvokerMap = null; // 置空列表

+            destroyAllInvokers(); // 关闭所有Invoker

+        } else {

+            this.forbidden = false; // 允许访问

+            List<URL> invokerUrls = new ArrayList<URL>();

+            List<URL> routerUrls = new ArrayList<URL>();

+            List<URL> overrideUrls = new ArrayList<URL>();

+            for (URL url : urls) {

+                if (Constants.ROUTE_PROTOCOL.equals(url.getProtocol())) {

+                    routerUrls.add(url);

+                } else if (Constants.OVERRIDE_PROTOCOL.equals(url.getProtocol())) {

+                    //url equals bug

+//                    if (! overrideUrls.contains(url)) {

+                        overrideUrls.add(url);

+//                    }

+                } else if (ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(url.getProtocol())) {

+//                    if (! invokerUrls.contains(url)) {

+                        invokerUrls.add(url);

+//                    }

+                } else {

+                    logger.error(new IllegalStateException("Unsupported protocol " + url.getProtocol() + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost() 

+                            + ", supported protocol: "+ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));

+                }

+            }

+            

+            //overrides 

+            if (overrideUrls != null && overrideUrls.size() >0 ){

+                overrideMap = toOverrides(overrideUrls);

+            }

+            

+            //route 

+            if (routerUrls != null && routerUrls.size() >0 ){

+                List<Router> routers = toRouters(routerUrls);

+                if(routers != null){ // null - do nothing

+                    setRouters(routers);

+                }

+            }

+            //invokers

+            refreshInvoker(invokerUrls);

+            

+            //合并override参数;

+            this.overrideDirectoryUrl = overrideMap == null ? directoryUrl : directoryUrl.addParameters(overrideMap.get(Constants.ANY_VALUE));

+        }

+    }

+    

+    

+    /**

+     * 根据invokerURL列表转换为invoker列表。转换规则如下:

+     * 1.如果url已经被转换为invoker,则不在重新引用,直接从缓存中获取,注意如果url中任何一个参数变更也会重新引用

+     * 2.如果传入的invoker列表不为空,则表示最新的invoker列表

+     * 3.如果传入的invokerUrl列表是空,则表示只是下发的override规则或route规则,需要重新交叉对比,决定是否需要重新引用。

+     * @param invokerUrls 传入的参数不能为null

+     */

+    private void refreshInvoker(List<URL> invokerUrls){

+        if (invokerUrls.size() == 0){

+            List<Invoker<T>> invokerList = new ArrayList<Invoker<T>>(urlInvokerMap.values());

+            for (Invoker<T> invoker : invokerList) {

+                URL url ;

+                if (invoker instanceof InvokerDelegete){

+                    url =  ((InvokerDelegete<T>)invoker).getProviderUrl();

+                } else {

+                    url = invoker.getUrl();

+                }

+                invokerUrls.add(url);

+            }

+        }

+        if (invokerUrls.size() ==0 ){

+        	return;

+        }

+        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表

+        Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表

+        Map<String, Invoker<T>> oldUrlInvokerMap = urlInvokerMap;

+        // state change

+        //如果计算错误,则不进行处理.

+        if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){

+            logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));

+            return ;

+        }

+        this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;

+        this.urlInvokerMap = newUrlInvokerMap;

+        try{

+            destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker

+        }catch (Exception e) {

+            logger.warn("destroyUnusedInvokers error. ", e);

+        }

+    }

+    

+    private Map<String, List<Invoker<T>>> toMergeMethodInvokerMap(Map<String, List<Invoker<T>>> methodMap) {

+        Map<String, List<Invoker<T>>> result = new HashMap<String, List<Invoker<T>>>();

+        for (Map.Entry<String, List<Invoker<T>>> entry : methodMap.entrySet()) {

+            String method = entry.getKey();

+            List<Invoker<T>> invokers = entry.getValue();

+            Map<String, List<Invoker<T>>> groupMap = new HashMap<String, List<Invoker<T>>>();

+            for (Invoker<T> invoker : invokers) {

+                String group = invoker.getUrl().getParameter(Constants.GROUP_KEY, "");

+                List<Invoker<T>> groupInvokers = groupMap.get(group);

+                if (groupInvokers == null) {

+                    groupInvokers = new ArrayList<Invoker<T>>();

+                    groupMap.put(group, groupInvokers);

+                }

+                groupInvokers.add(invoker);

+            }

+            if (groupMap.size() == 1) {

+                result.put(method, groupMap.values().iterator().next());

+            } else if (groupMap.size() > 1) {

+                List<Invoker<T>> groupInvokers = new ArrayList<Invoker<T>>();

+                for (List<Invoker<T>> groupList : groupMap.values()) {

+                    groupInvokers.add(cluster.join(new StaticDirectory<T>(groupList)));

+                }

+                result.put(method, groupInvokers);

+            } else {

+                result.put(method, invokers);

+            }

+        }

+        return result;

+    }

+    

+    /**

+     * 将overrideURL转换为map,供重新refer时使用.

+     * 每次下发全部规则,全部重新组装计算

+     * @param urls

+     * 契约:

+     * </br>1.override://0.0.0.0/...(或override://ip:port...?anyhost=true)&para1=value1...表示全局规则(对所有的提供者全部生效)

+     * </br>2.override://ip:port...?anyhost=false 特例规则(只针对某个提供者生效)

+     * </br>3.不支持override://规则... 需要注册中心自行计算.

+     * </br>4.不带参数的override://0.0.0.0/ 表示清除override 

+     * @return

+     */

+    private Map<String, Map<String, String>> toOverrides(List<URL> urls){

+        if (urls == null || urls.size() == 0){

+            return null;

+        }

+        Map<String, Map<String, String>> overrides = new ConcurrentHashMap<String, Map<String,String>>(urls.size());

+        for(URL url : urls){

+            Map<String,String> override = new HashMap<String, String>(url.getParameters());

+            //override 上的anyhost可能是自动添加的,不能影响改变url判断

+            override.remove(Constants.ANYHOST_KEY);

+            if (override.size() == 0){

+                overrides.clear();

+                continue;

+            }

+            if (url.isAnyHost()){

+                overrides.put(Constants.ANY_VALUE, override);

+            } else {

+                //需要ip:port 一个ip可能启动多个服务实例

+                overrides.put(url.getAddress(), override);

+            }

+        }

+        return overrides;

+    }

+    

+    /**

+     * 

+     * @param urls

+     * @return null : no routers ,do nothing

+     *         else :routers list

+     */

+    private List<Router> toRouters(List<URL> urls) {

+        //no router urls , do nothing

+        if(urls == null || urls.size() < 1){

+            return null ;

+        }

+        List<Router> routers = new ArrayList<Router>();

+        

+        // on these conditions: clear all current routers

+        // 1. there is only one route url

+        // 2. with type = clear

+        if(urls.size() == 1){

+           URL u = urls.get(0);

+           // clean current routers

+           if(Constants.ROUTER_TYPE_CLEAR.equals(u.getParameter(Constants.ROUTER_KEY))){

+               return routers;

+           }

+        }

+        

+        if (urls != null && urls.size() > 0) {

+            for (URL url : urls) {

+                String router_type = url.getParameter(Constants.ROUTER_KEY, ScriptRouterFactory.NAME);

+                if (router_type == null || router_type.length() == 0){

+                    logger.warn("Router url:\"" + url.toString() + "\" does not contain " + Constants.ROUTER_KEY + ", router creation ignored!");

+                    continue;

+                }

+                try{

+                Router router = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(router_type).getRouter(url);

+                if (!routers.contains(router))

+                    routers.add(router);

+                }catch (Throwable t) {

+                    logger.error("convert router url to router error, url: "+ url, t);

+                }

+            }

+        }

+        return routers;

+    }

+    

+    /**

+     * 将urls转成invokers,如果url已经被refer过,不再重新引用。

+     * 

+     * @param urls

+     * @param overrides

+     * @param query

+     * @return invokers

+     */

+    private Map<String, Invoker<T>> toInvokers(List<URL> urls) {

+        if(urls == null || urls.size() == 0){

+            return null;

+        }

+        Map<String, Invoker<T>> newUrlInvokerMap = new ConcurrentHashMap<String, Invoker<T>>();

+        Set<String> keys = new HashSet<String>();

+        for (URL providerUrl : urls) {

+            URL url = mergeUrl(providerUrl);

+            

+            String key = url.toFullString(); // URL参数是排序的

+            if (keys.contains(key)) { // 重复URL

+                continue;

+            }

+            keys.add(key);

+            // 缓存key为没有合并消费端参数的URL,不管消费端如何合并参数,如果服务端URL发生变化,则重新refer

+            Invoker<T> invoker = urlInvokerMap.get(key);

+            if (invoker == null) { // 缓存中没有,重新refer

+                try {

+                    invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);

+                } catch (Throwable t) {

+                    logger.error("Failed to refer invoker for interface:"+serviceType+",url:("+url+")" + t.getMessage(), t);

+                }

+                if (invoker != null) { // 将新的引用放入缓存

+                    newUrlInvokerMap.put(key, invoker);

+                }

+            }else {

+                newUrlInvokerMap.put(key, invoker);

+            }

+        }

+        keys.clear();

+        return newUrlInvokerMap;

+    }

+    

+    /**

+     * 合并url参数 顺序为override > -D >Consumer > Provider

+     * @param providerUrl

+     * @param overrides

+     * @return

+     */

+    private URL mergeUrl(URL providerUrl){

+        Map<String, String> alloverride = overrideMap == null ? new HashMap<String,String>() : overrideMap.get(Constants.ANY_VALUE);

+        providerUrl = ClusterUtils.mergeUrl(providerUrl, queryMap); // 合并消费端参数

+        providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // 不检查连接是否成功,总是创建Invoker!

+        

+        //directoryUrl 与 override 合并是在notify的最后,这里不能够处理

+        this.directoryUrl = this.directoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // 合并提供者参数        

+        

+        //先做全局覆盖

+        providerUrl = providerUrl.addParameters(alloverride);//合并override参数

+        //然后做特定覆盖

+        Map<String, String> oneOverride = overrideMap == null ? null : overrideMap.get(providerUrl.getAddress());

+        if(oneOverride != null && overrideMap.get(providerUrl.getAddress()).size() > 0){

+            providerUrl = providerUrl.addParameters(oneOverride);//合并override参数

+        }

+        

+        if ((providerUrl.getPath() == null || providerUrl.getPath().length() == 0)

+                && "dubbo".equals(providerUrl.getProtocol())) { // 兼容1.0

+            //fix by tony.chenl DUBBO-44

+            String path = directoryUrl.getParameter(Constants.INTERFACE_KEY);

+            int i = path.indexOf('/');

+            if (i >= 0) {

+                path = path.substring(i + 1);

+            }

+            i = path.lastIndexOf(':');

+            if (i >= 0) {

+                path = path.substring(0, i);

+            }

+            providerUrl = providerUrl.setPath(path);

+        }

+        return providerUrl;

+    }

+

+    /**

+     * 将invokers列表转成与方法的映射关系

+     * 

+     * @param invokersMap Invoker列表

+     * @return Invoker与方法的映射关系

+     */

+    private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) {

+        Map<String, List<Invoker<T>>> methodInvokerMap = new HashMap<String, List<Invoker<T>>>();

+        if (invokersMap != null && invokersMap.size() > 0) {

+            List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();

+            for (Invoker<T> invoker : invokersMap.values()) {

+                String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);

+                if (parameter != null && parameter.length() > 0) {

+                    String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);

+                    if (methods != null && methods.length > 0) {

+                        for (String method : methods) {

+                            if (method != null && method.length() > 0 

+                                    && ! Constants.ANY_VALUE.equals(method)) {

+                                List<Invoker<T>> methodInvokers = methodInvokerMap.get(method);

+                                if (methodInvokers == null) {

+                                    methodInvokers = new ArrayList<Invoker<T>>();

+                                    methodInvokerMap.put(method, methodInvokers);

+                                }

+                                methodInvokers.add(invoker);

+                            }

+                        }

+                    }

+                }

+                invokersList.add(invoker);

+            }

+            methodInvokerMap.put(Constants.ANY_VALUE, invokersList);

+        }

+        // sort and unmodifiable

+        for (String method : new HashSet<String>(methodInvokerMap.keySet())) {

+            List<Invoker<T>> methodInvokers = methodInvokerMap.get(method);

+            Collections.sort(methodInvokers, InvokerComparator.getComparator());

+            methodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));

+        }

+        return Collections.unmodifiableMap(methodInvokerMap);

+    }

+

+    /**

+     * 关闭所有Invoker

+     */

+    private void destroyAllInvokers() {

+        if(urlInvokerMap != null) {

+            for (Invoker<T> invoker : new ArrayList<Invoker<T>>(urlInvokerMap.values())) {

+                try {

+                    invoker.destroy();

+                } catch (Throwable t) {

+                    logger.warn("Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t);

+                }

+            }

+            urlInvokerMap.clear();

+        }

+        methodInvokerMap = null;

+    }

+    

+    /**

+     * 检查缓存中的invoker是否需要被destroy

+     * 如果url中指定refer.autodestroy=false,则只增加不减少,可能会有refer泄漏,

+     * 

+     * @param invokers

+     */

+    private void destroyUnusedInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, Map<String, Invoker<T>> newUrlInvokerMap) {

+        if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {

+            destroyAllInvokers();

+            return;

+        }

+        // check deleted invoker

+        List<String> deleted = null;

+        if (oldUrlInvokerMap != null) {

+            Collection<Invoker<T>> newInvokers = newUrlInvokerMap.values();

+            for (Map.Entry<String, Invoker<T>> entry : oldUrlInvokerMap.entrySet()){

+                if (! newInvokers.contains(entry.getValue())) {

+                    if (deleted == null) {

+                        deleted = new ArrayList<String>();

+                    }

+                    deleted.add(entry.getKey());

+                }

+            }

+        }

+        

+        if (deleted != null) {

+            for (String url : deleted){

+                if (url != null ) {

+                    Invoker<T> invoker = oldUrlInvokerMap.remove(url);

+                    if (invoker != null) {

+                        try {

+                            invoker.destroy();

+                            if(logger.isDebugEnabled()){

+                                logger.debug("destory invoker["+invoker.getUrl()+"] success. ");

+                            }

+                        } catch (Exception e) {

+                            logger.warn("destory invoker["+invoker.getUrl()+"] faild. " + e.getMessage(), e);

+                        }

+                    }

+                }

+            }

+        }

+    }

+

+    public List<Invoker<T>> doList(Invocation invocation) {

+        if (forbidden) {

+            throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "Forbid consumer " +  NetUtils.getLocalHost() + " access service " + getInterface().getName() + " from registry " + getUrl().getAddress() + " use dubbo version " + Version.getVersion() + ", Please check registry access list (whitelist/blacklist).");

+        }

+        List<Invoker<T>> invokers = null;

+        if (methodInvokerMap != null && methodInvokerMap.size() > 0) {

+            String methodName = invocation.getMethodName();

+            Object[] args = invocation.getArguments();

+            

+            // Generic invoke: Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;

+            if (Constants.$INVOKE.equals(methodName) 

+                    && args != null && args.length == 3

+                    && args[0] instanceof String

+                    && args[2] instanceof Object[]) { 

+                methodName = (String) args[0];

+                args = (Object[]) args[2];

+            }

+            if(args != null && args.length > 0 && args[0] != null

+                    && (args[0] instanceof String || args[0].getClass().isEnum())) {

+                invokers = methodInvokerMap.get(methodName + "." + args[0]); // 可根据第一个参数枚举路由

+            }

+            if(invokers == null) {

+                invokers = methodInvokerMap.get(methodName);

+            }

+            if(invokers == null) {

+                invokers = methodInvokerMap.get(Constants.ANY_VALUE);

+            }

+            if(invokers == null) {

+                Iterator<List<Invoker<T>>> iterator = methodInvokerMap.values().iterator();

+                if (iterator.hasNext()) {

+                    invokers = iterator.next();

+                }

+            }

+        }

+        return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;

+    }

+    

+    public Class<T> getInterface() {

+        return serviceType;

+    }

+

+    public URL getUrl() {

+    	return this.overrideDirectoryUrl;

+    }

+

+    public boolean isAvailable() {

+        if (destroyed) return false;

+        Map<String, Invoker<T>> map = urlInvokerMap;

+        if (map != null && map.size() > 0) {

+            for (Invoker<T> invoker : new ArrayList<Invoker<T>>(map.values())) {

+                if (invoker.isAvailable()) {

+                    return true;

+                }

+            }

+        }

+        return false;

+    }

+    

+    /**

+     * Haomin: added for test purpose

+     */

+    public Map<String, Invoker<T>> getUrlInvokerMap(){

+        return urlInvokerMap;

+    }

+    

+    /**

+     * Haomin: added for test purpose

+     */

+    public Map<String, List<Invoker<T>>> getMethodInvokerMap(){

+        return methodInvokerMap;

+    } 

+    

+    private static class InvokerComparator implements Comparator<Invoker<?>> {

+        

+        private static final InvokerComparator comparator = new InvokerComparator();

+        

+        public static InvokerComparator getComparator() {

+            return comparator;

+        }

+        

+        private InvokerComparator() {}

+

+        public int compare(Invoker<?> o1, Invoker<?> o2) {

+            return o1.getUrl().toString().compareTo(o2.getUrl().toString());

+        }

+

+    }

+    

+    /**

+     * 代理类,主要用于存储注册中心下发的url地址,用于重新重新refer时能够根据providerURL queryMap overrideMap重新组装

+     * 

+     * @author chao.liuc

+     *

+     * @param <T>

+     */

+    private static class InvokerDelegete<T> extends InvokerWrapper<T>{

+        private URL providerUrl;

+        public InvokerDelegete(Invoker<T> invoker, URL url, URL providerUrl) {

+            super(invoker, url);

+            this.providerUrl = providerUrl;

+        }

+        public URL getProviderUrl() {

+            return providerUrl;

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryProtocol.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryProtocol.java
new file mode 100644
index 0000000..292b191
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryProtocol.java
@@ -0,0 +1,379 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.support;

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.RegistryFactory;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.cluster.Cluster;

+import com.alibaba.dubbo.rpc.protocol.InvokerWrapper;

+

+/**

+ * RegistryProtocol

+ * 

+ * @author william.liangf

+ * @author chao.liuc

+ */

+public class RegistryProtocol implements Protocol {

+

+    private Cluster cluster;

+    

+    public void setCluster(Cluster cluster) {

+        this.cluster = cluster;

+    }

+    

+    private Protocol protocol;

+    

+    public void setProtocol(Protocol protocol) {

+        this.protocol = protocol;

+    }

+

+    private RegistryFactory registryFactory;

+    

+    public void setRegistryFactory(RegistryFactory registryFactory) {

+        this.registryFactory = registryFactory;

+    }

+

+    public int getDefaultPort() {

+        return 9090;

+    }

+    

+    private static RegistryProtocol INSTANCE;

+

+    public RegistryProtocol() {

+        INSTANCE = this;

+    }

+    

+    public static RegistryProtocol getRegistryProtocol() {

+        if (INSTANCE == null) {

+            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(Constants.REGISTRY_PROTOCOL); // load

+        }

+        return INSTANCE;

+    }

+    

+    //用于解决rmi重复暴露端口冲突的问题,已经暴露过的服务不再重新暴露

+    //providerurl <--> exporter

+    private final Map<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<String, ExporterChangeableWrapper<?>>();

+    

+    private final NotifyListener listener = new OverrideListener();

+    

+    private final static Logger logger = LoggerFactory.getLogger(RegistryProtocol.class);

+    

+    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {

+        //export invoker

+        final ExporterChangeableWrapper<T>  exporter = doLocolExport(originInvoker);

+        //registry provider

+        Registry registry = doRegister(originInvoker);

+        //设置exporter与registry的关系 (for unexport)

+        exporter.setRegistry(registry);

+        //保证每次export都返回一个新的exporter实例

+        return new Exporter<T>() {

+            public Invoker<T> getInvoker() {

+                return exporter.getInvoker();

+            }

+            public void unexport() {

+                exporter.unexport();

+            }

+        };

+    }

+    

+    @SuppressWarnings("unchecked")

+    private <T> ExporterChangeableWrapper<T>  doLocolExport(final Invoker<T> originInvoker){

+        String key = getCacheKey(originInvoker);

+        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);

+        if (exporter == null) {

+            synchronized (bounds) {

+                exporter = (ExporterChangeableWrapper<T>) bounds.get(key);

+                if (exporter == null) {

+                    final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));

+                    exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);

+                    bounds.put(key, exporter);

+                }

+            }

+        }

+        return (ExporterChangeableWrapper<T>) exporter;

+    }

+    

+    /**

+     * 对修改了url的invoker重新export

+     * @param originInvoker

+     * @param newInvokerUrl

+     */

+    @SuppressWarnings("unchecked")

+    private <T> void doChangeLocolExport(final Invoker<T> originInvoker, URL newInvokerUrl){

+        String key = getCacheKey(originInvoker);

+        final ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);

+        if (exporter == null){

+            logger.warn(new IllegalStateException("error state, exporter should not be null"));

+            return ;//不存在是异常场景 直接返回 

+        } else {

+            final Invoker<T> invokerDelegete = new InvokerDelegete<T>(originInvoker, newInvokerUrl);

+            exporter.setExporter(protocol.export(invokerDelegete));

+        }

+    }

+

+    /**

+     * 注册 provider

+     * @param originInvoker

+     * @return

+     */

+    private Registry doRegister(final Invoker<?> originInvoker){

+        final Registry registry = getRegistry(originInvoker);

+        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);

+        registry.register(registedProviderUrl, listener);

+        return registry;

+    }

+

+    /**

+     * 根据invoker的地址获取registry实例

+     * @param originInvoker

+     * @return

+     */

+    private Registry getRegistry(final Invoker<?> originInvoker){

+        URL registryUrl = originInvoker.getUrl();

+        if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {

+            String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);

+            registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);

+        }

+        return registryFactory.getRegistry(registryUrl);

+    }

+

+    /**

+     * 返回注册到注册中心的URL,对URL参数进行一次过滤

+     * @param originInvoker

+     * @return

+     */

+    private URL getRegistedProviderUrl(final Invoker<?> originInvoker){

+        URL providerUrl = getProviderUrl(originInvoker);

+        //注册中心看到的地址

+        final URL registedProviderUrl = providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameter(Constants.MONITOR_KEY);

+        return registedProviderUrl;

+    }

+

+    /**

+     * 通过invoker的url 获取 providerUrl的地址

+     * @param origininvoker

+     * @return

+     */

+    private URL getProviderUrl(final Invoker<?> origininvoker){

+        String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY);

+        if (export == null || export.length() == 0) {

+            throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl());

+        }

+        

+        URL providerUrl = URL.valueOf(export);

+        return providerUrl;

+    }

+

+    /**

+     * 获取invoker在bounds中缓存的key

+     * @param originInvoker

+     * @return

+     */

+    private String getCacheKey(final Invoker<?> originInvoker){

+        URL providerUrl = getProviderUrl(originInvoker);

+        String key = providerUrl.removeParameters("dynamic", "enabled").toFullString();

+        return key;

+    }

+    

+    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {

+        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);

+        Registry registry = registryFactory.getRegistry(url);

+

+        // group="a,b" or group="*"

+        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));

+        String group = qs.get(Constants.GROUP_KEY);

+        if (group != null && group.length() > 0 ) {

+            if ( ( Constants.COMMA_SPLIT_PATTERN.split( group ) ).length > 1

+                    || "*".equals( group ) ) {

+                return doRefer( getMergeableCluster(), registry, type, url );

+            }

+        }

+        return doRefer(cluster, registry, type, url);

+    }

+    

+    private Cluster getMergeableCluster() {

+        return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("mergeable");

+    }

+    

+    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {

+        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);

+        directory.setRegistry(registry);

+        directory.setProtocol(protocol);

+        registry.subscribe(new URL(Constants.SUBSCRIBE_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters()), directory);

+        return cluster.join(directory);

+    }

+

+    //过滤URL中不需要输出的参数(以点号开头的)

+    private static String[] getFilteredKeys(URL url) {

+        Map<String, String> params = url.getParameters();

+        if (params != null && !params.isEmpty()) {

+            List<String> filteredKeys = new ArrayList<String>();

+            for (Map.Entry<String, String> entry : params.entrySet()) {

+                if (entry != null && entry.getKey() != null && entry.getKey().startsWith(Constants.HIDE_KEY_PREFIX)) {

+                    filteredKeys.add(entry.getKey());

+                }

+            }

+            return filteredKeys.toArray(new String[filteredKeys.size()]);

+        } else {

+            return new String[] {};

+        }

+    }

+    

+    public void destroy() {

+        List<Exporter<?>> exporters = new ArrayList<Exporter<?>>(bounds.values());

+        for(Exporter<?> exporter :exporters){

+            exporter.unexport();

+        }

+        bounds.clear();

+    }

+    

+    

+    /*重新export 1.protocol中的exporter destory问题 

+     *1.要求registryprotocol返回的exporter可以正常destroy

+     *2.notify后不需要重新向注册中心注册 

+     *3.export 方法传入的invoker最好能一直作为exporter的invoker.

+     */

+    private class OverrideListener implements NotifyListener {

+

+        /*

+         *  provider 端可识别的override url只有这两种.

+         *  override://0.0.0.0/serviceName?timeout=10

+         *  override://0.0.0.0/?timeout=10

+         */

+        public void notify(List<URL> urls) {

+            List<ExporterChangeableWrapper<?>> exporters = new ArrayList<ExporterChangeableWrapper<?>>(bounds.values());

+            for (ExporterChangeableWrapper<?> exporter : exporters){

+                Invoker<?> invoker = exporter.getOriginInvoker();

+                final Invoker<?> originInvoker ;

+                if (invoker instanceof InvokerDelegete){

+                    originInvoker = ((InvokerDelegete<?>)invoker).getInvoker();

+                }else {

+                    originInvoker = invoker;

+                }

+                

+                URL originUrl = RegistryProtocol.this.getProviderUrl(originInvoker);

+                URL newUrl = getNewInvokerUrl(originUrl, urls);

+                

+                if (! originUrl.toFullString().equals(newUrl.toFullString())){

+                    RegistryProtocol.this.doChangeLocolExport(originInvoker, newUrl);

+                }

+            }

+        }

+        

+        private URL getNewInvokerUrl(final URL originUrl, final List<URL> urls){

+            URL newUrl = originUrl;

+            //override://0.0.0.0/?timeout=10 ip:port无意义

+            for (URL overrideUrl : urls){

+                if (overrideUrl.getServiceInterface() == null){

+                    newUrl = newUrl.addParameters(overrideUrl.getParameters());

+                }

+            }

+            //override://0.0.0.0/serviceName?timeout=10

+            for (URL overrideUrl : urls){

+                if (originUrl.getServiceKey().equals(overrideUrl.getServiceKey())){

+                    newUrl = newUrl.addParameters(overrideUrl.getParameters());

+                }

+            }

+            

+            return newUrl;

+        }

+    }

+    

+    public static class InvokerDelegete<T> extends InvokerWrapper<T>{

+        private final Invoker<T> invoker;

+        /**

+         * @param invoker 

+         * @param url invoker.getUrl返回此值

+         */

+        public InvokerDelegete(Invoker<T> invoker, URL url){

+            super(invoker, url);

+            this.invoker = invoker;

+        }

+        public Invoker<T> getInvoker(){

+            if (invoker instanceof InvokerDelegete){

+                return ((InvokerDelegete<T>)invoker).getInvoker();

+            } else {

+                return invoker;

+            }

+        }

+    }

+    

+    /**

+     * exporter代理,建立返回的exporter与protocol export出的exporter的对应关系,在override时可以进行关系修改.

+     * 

+     * @author chao.liuc

+     *

+     * @param <T>

+     */

+    private class ExporterChangeableWrapper<T> implements Exporter<T>{

+        private Exporter<T> exporter;

+        private final Invoker<T> originInvoker;

+        private Registry registry;

+

+        public ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker){

+            this.exporter = exporter;

+            this.originInvoker = originInvoker;

+        }

+        

+        public Invoker<T> getOriginInvoker() {

+            return originInvoker;

+        }

+

+        public Invoker<T> getInvoker() {

+            return exporter.getInvoker();

+        }

+        

+        public void setExporter(Exporter<T> exporter){

+            this.exporter = exporter;

+        }

+        

+        public void setRegistry(final Registry registry) {

+            if (this.registry != null){

+                logger.warn(new IllegalStateException("registry can not be changed!"));

+            } 

+            this.registry = registry;

+        }

+

+        public void unexport() {

+            String key = getCacheKey(this.originInvoker);

+            bounds.remove(key);

+            try {

+                if (registry != null && registry.isAvailable()) {

+                    registry.unregister(getRegistedProviderUrl(originInvoker), listener);

+                }

+            } finally {

+                exporter.unexport();

+            }

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryStatusChecker.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryStatusChecker.java
new file mode 100644
index 0000000..23a74bf
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryStatusChecker.java
@@ -0,0 +1,55 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.support;

+

+import java.util.Collection;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.registry.Registry;

+

+/**

+ * RegistryStatusChecker

+ * 

+ * @author william.liangf

+ */

+@Activate

+public class RegistryStatusChecker implements StatusChecker {

+

+    public Status check() {

+        Collection<Registry> regsitries = AbstractRegistryFactory.getRegistries();

+        if (regsitries == null || regsitries.size() == 0) {

+            return new Status(Status.Level.UNKNOWN);

+        }

+        Status.Level level = Status.Level.OK;

+        StringBuilder buf = new StringBuilder();

+        for (Registry registry : regsitries) {

+            if (buf.length() > 0) {

+                buf.append(",");

+            }

+            buf.append(registry.getUrl().getAddress());

+            if (! registry.isAvailable()) {

+                level = Status.Level.ERROR;

+                buf.append("(disconnected)");

+            } else {

+                buf.append("(connected)");

+            }

+        }

+        return new Status(level, buf.toString());

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryExporter.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryExporter.java
new file mode 100644
index 0000000..a7ff565
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryExporter.java
@@ -0,0 +1,66 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.support;

+

+import java.io.IOException;

+import java.net.ServerSocket;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.registry.RegistryService;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+

+/**

+ * SimpleRegistryExporter

+ * 

+ * @author william.liangf

+ */

+public class SimpleRegistryExporter {

+    

+    private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+    

+    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+    

+    public synchronized static Exporter<RegistryService> exportIfAbsent(int port) {

+        try {

+            new ServerSocket(port).close();

+            return export(port);

+        } catch (IOException e) {

+            return null;

+        }

+    }

+    

+    public static Exporter<RegistryService> export(int port) {

+        return export(port, new SimpleRegistryService());

+    }

+    

+    public static Exporter<RegistryService> export(int port, RegistryService registryService) {

+        return protocol.export(proxyFactory.getInvoker(registryService, RegistryService.class, 

+                new URL("dubbo", NetUtils.getLocalHost(), port, RegistryService.class.getName())

+                .setPath(RegistryService.class.getName())

+                .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())

+                .addParameter(Constants.CLUSTER_STICKY_KEY, "true")

+                .addParameter(Constants.CALLBACK_INSTANCES_LIMIT_KEY, "1000")

+                .addParameter("ondisconnect", "disconnect")

+                .addParameter("subscribe.1.callback", "true")

+                .addParameter("unsubscribe.1.callback", "false")));

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryService.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryService.java
new file mode 100644
index 0000000..fa681f9
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryService.java
@@ -0,0 +1,153 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry.support;

+

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.UrlUtils;

+import com.alibaba.dubbo.registry.NotifyListener;

+import com.alibaba.dubbo.registry.RegistryService;

+import com.alibaba.dubbo.rpc.RpcContext;

+

+/**

+ * SimpleRegistryService

+ * 

+ * @author william.liangf

+ */

+public class SimpleRegistryService extends AbstractRegistryService {

+

+    private final ConcurrentMap<String, ConcurrentMap<String, URL>> remoteRegistered = new ConcurrentHashMap<String, ConcurrentMap<String, URL>>();

+

+    private final ConcurrentMap<String, ConcurrentMap<String, NotifyListener>> remoteListeners = new ConcurrentHashMap<String, ConcurrentMap<String, NotifyListener>>();

+    

+    private final static Logger logger = LoggerFactory.getLogger(SimpleRegistryService.class);

+

+    private List<String> registries;

+    

+    @Override

+    public void register(String service, URL url, NotifyListener listener) {

+        super.register(service, url, listener);

+        String client = RpcContext.getContext().getRemoteAddressString();

+        Map<String, URL> urls = remoteRegistered.get(client);

+        if (urls == null) {

+            remoteRegistered.putIfAbsent(client, new ConcurrentHashMap<String, URL>());

+            urls = remoteRegistered.get(client);

+        }

+        urls.put(service, url);

+        notify(service, getRegistered().get(service));

+    }

+

+    @Override

+    public void unregister(String service, URL url, NotifyListener listener) {

+        super.unregister(service, url, listener);

+        String client = RpcContext.getContext().getRemoteAddressString();

+        Map<String, URL> urls = remoteRegistered.get(client);

+        if (urls != null && urls.size() > 0) {

+            urls.remove(service);

+        }

+        notify(service, getRegistered().get(service));

+    }

+

+    @Override

+    public void subscribe(String service, URL url, NotifyListener listener) {

+        String client = RpcContext.getContext().getRemoteAddressString();

+        if (logger.isInfoEnabled()){

+            logger.info("[subscribe] service: "+service + ",client:"+ client);

+        }

+        List<URL> urls = getRegistered().get(service);

+        if ((RegistryService.class.getName() + ":0.0.0").equals(service)

+                && (urls == null || urls.size() == 0)) {

+            register(service, new URL("dubbo", 

+                    NetUtils.getLocalHost(), 

+                    RpcContext.getContext().getLocalPort(), 

+                    com.alibaba.dubbo.registry.RegistryService.class.getName(), 

+                    url.getParameters()), null);

+            List<String> rs = registries;

+            if (rs != null && rs.size() > 0) {

+                for (String registry : rs) {

+                    register(service, UrlUtils.parseURL(registry, url.getParameters()), null);

+                }

+            }

+        }

+        super.subscribe(service, url, listener);

+        

+        Map<String, NotifyListener> listeners = remoteListeners.get(client);

+        if (listeners == null) {

+            remoteListeners.putIfAbsent(client, new ConcurrentHashMap<String, NotifyListener>());

+            listeners = remoteListeners.get(client);

+        }

+        listeners.put(service, listener);

+        urls = getRegistered().get(service);

+        if (urls != null && urls.size() > 0) {

+            listener.notify(urls);

+        }

+        

+        

+    }

+

+    @Override

+    public void unsubscribe(String service, URL url, NotifyListener listener) {

+        super.unsubscribe(service, url, listener);

+        String client = RpcContext.getContext().getRemoteAddressString();

+        Map<String, NotifyListener> listeners = remoteListeners.get(client);

+        if (listeners != null && listeners.size() > 0) {

+            listeners.remove(service);

+        }

+        List<URL> urls = getRegistered().get(service);

+        if (urls != null && urls.size() > 0) {

+            listener.notify(urls);

+        }

+    }

+    

+    public void disconnect() {

+        String client = RpcContext.getContext().getRemoteAddressString();

+        if (logger.isInfoEnabled()) {

+            logger.info("Disconnected " + client);

+        }

+        ConcurrentMap<String, URL> urls = remoteRegistered.get(client);

+        if (urls != null && urls.size() > 0) {

+            for (Map.Entry<String, URL> entry : urls.entrySet()) {

+                super.unregister(entry.getKey(), entry.getValue(), null);

+            }

+        }

+        Map<String, NotifyListener> listeners = remoteListeners.get(client);

+        if (listeners != null && listeners.size() > 0) {

+            for (Map.Entry<String, NotifyListener> entry : listeners.entrySet()) {

+                String service = entry.getKey();

+                super.unsubscribe(service, new URL("subscribe", 

+                        RpcContext.getContext().getRemoteHost(), 

+                        RpcContext.getContext().getRemotePort(), 

+                        com.alibaba.dubbo.registry.RegistryService.class.getName(), getSubscribed(service)), entry.getValue());

+            }

+        }

+    }

+

+    public List<String> getRegistries() {

+        return registries;

+    }

+

+    public void setRegistries(List<String> registries) {

+        this.registries = registries;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-registry/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..b7241be
--- /dev/null
+++ b/dubbo-registry/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1 @@
+registry=com.alibaba.dubbo.registry.support.RegistryStatusChecker
\ No newline at end of file
diff --git a/dubbo-registry/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler b/dubbo-registry/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..8e75ea0
--- /dev/null
+++ b/dubbo-registry/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,3 @@
+registries=com.alibaba.dubbo.registry.pages.RegistriesPageHandler

+registered=com.alibaba.dubbo.registry.pages.RegisteredPageHandler

+subscribed=com.alibaba.dubbo.registry.pages.SubscribedPageHandler
\ No newline at end of file
diff --git a/dubbo-registry/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-registry/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..4735fad
--- /dev/null
+++ b/dubbo-registry/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+registry=com.alibaba.dubbo.registry.support.RegistryProtocol
\ No newline at end of file
diff --git a/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/AbstractRegistryFactoryTest.java b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/AbstractRegistryFactoryTest.java
new file mode 100644
index 0000000..89582c5
--- /dev/null
+++ b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/AbstractRegistryFactoryTest.java
@@ -0,0 +1,87 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry;

+

+import java.util.List;

+

+import junit.framework.Assert;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+

+/**

+ * AbstractRegistryFactoryTest

+ * 

+ * @author william.liangf

+ */

+public class AbstractRegistryFactoryTest {

+    

+    private RegistryFactory registryFactory = new AbstractRegistryFactory() {

+        

+        @Override

+        protected Registry createRegistry(final URL url) {

+            return new Registry() {

+

+                public URL getUrl() {

+                    return url;

+                }

+

+                public boolean isAvailable() {

+                    return false;

+                }

+

+                public void destroy() {

+                }

+

+                public void register(URL url, NotifyListener listener) {

+                }

+

+                public void unregister(URL url, NotifyListener listener) {

+                }

+

+                public void subscribe(URL url, NotifyListener listener) {

+                }

+

+                public void unsubscribe(URL url, NotifyListener listener) {

+                }

+

+                public List<URL> lookup(URL url) {

+                    return null;

+                }

+                

+            };

+        }

+    };

+    

+    @Test

+    public void testRegistryFactoryCache() throws Exception {

+        URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":2233");

+        Registry registry1 = registryFactory.getRegistry(url);

+        Registry registry2 = registryFactory.getRegistry(url);

+        Assert.assertEquals(registry1, registry2);

+    }

+    

+    @Test

+    public void testRegistryFactoryIpCache() throws Exception {

+        Registry registry1 = registryFactory.getRegistry(URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":2233"));

+        Registry registry2 = registryFactory.getRegistry(URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":2233"));

+        Assert.assertEquals(registry1, registry2);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceRegistryTest.java b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceRegistryTest.java
new file mode 100644
index 0000000..50053be
--- /dev/null
+++ b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceRegistryTest.java
@@ -0,0 +1,69 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry;

+

+import junit.framework.TestCase;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+

+/**

+ * RegistryPerformanceTest

+ * 

+ * @author william.liangf

+ */

+public class PerformanceRegistryTest extends TestCase {

+

+    private static final Logger logger = LoggerFactory.getLogger(PerformanceRegistryTest.class);

+

+    @Test

+    public void testRegistry() {

+        // 读取参数

+        if (PerformanceUtils.getProperty("server", null) == null) {

+            logger.warn("Please set -Dserver=127.0.0.1:9090");

+            return;

+        }

+        final int base = PerformanceUtils.getIntProperty("base", 0);

+        final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100);

+        int r = PerformanceUtils.getIntProperty("runs", 1000);

+        final int runs = r > 0 ? r : Integer.MAX_VALUE;

+        final Registry registry = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(URL.valueOf("remote://admin:hello1234@" + PerformanceUtils.getProperty("server", "10.20.153.28:9090")));

+        for (int i = 0; i < concurrent; i ++) {

+            final int t = i;

+            new Thread(new Runnable() {

+                public void run() {

+                    for (int j = 0; j < runs; j ++) {

+                        registry.register(URL.valueOf("remote://" + NetUtils.getLocalHost() + ":8080/demoService" + t + "_" + j + "?version=1.0.0&application=demo&dubbo=2.0&interface=" + "com.alibaba.dubbo.demo.DemoService" + (base + t) + "_" + (base + j)), null);

+                    }

+                }

+            }).start();

+        }

+        synchronized (PerformanceRegistryTest.class) {

+            while (true) {

+                try {

+                    PerformanceRegistryTest.class.wait();

+                } catch (InterruptedException e) {

+                }

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceUtils.java b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceUtils.java
new file mode 100644
index 0000000..1e6683c
--- /dev/null
+++ b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceUtils.java
@@ -0,0 +1,126 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry;

+

+import java.net.NetworkInterface;

+import java.net.SocketException;

+import java.text.DecimalFormat;

+import java.util.ArrayList;

+import java.util.Enumeration;

+import java.util.List;

+

+/**

+ * PerformanceUtils

+ * 

+ * @author william.liangf

+ */

+public class PerformanceUtils {

+

+    public static String getProperty(String key, String defaultValue) {

+        String value = System.getProperty(key);

+        if (value == null || value.trim().length() == 0 || value.startsWith("$")) {

+            return defaultValue;

+        }

+        return value.trim();

+    }

+    

+    public static int getIntProperty(String key, int defaultValue) {

+        String value = System.getProperty(key);

+        if (value == null || value.trim().length() == 0 || value.startsWith("$")) {

+            return defaultValue;

+        }

+        return Integer.parseInt(value.trim());

+    }

+    public static boolean getBooleanProperty(String key, boolean defaultValue) {

+        String value = System.getProperty(key);

+        if (value == null || value.trim().length() == 0 || value.startsWith("$")) {

+            return defaultValue;

+        }

+        return Boolean.parseBoolean(value.trim());

+    }

+    

+    public static List<String> getEnvironment() {

+        List<String> environment = new ArrayList<String>();

+        environment.add("OS: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch", ""));

+        environment.add("CPU: " + Runtime.getRuntime().availableProcessors() + " cores");

+        environment.add("JVM: " + System.getProperty("java.vm.name") + " " + System.getProperty("java.runtime.version"));

+        environment.add("Memory: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().totalMemory()) 

+                                   + " bytes (Max: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().maxMemory()) + " bytes)");

+        NetworkInterface ni = PerformanceUtils.getNetworkInterface();

+        if (ni != null) {

+            environment.add("Network: " + ni.getDisplayName());

+        }

+        return environment;

+    }

+

+    private static final int WIDTH = 64;

+    

+    public static void printSeparator() {

+        StringBuilder pad = new StringBuilder();

+        for (int i = 0; i < WIDTH; i ++) {

+            pad.append("-");

+        }

+        System.out.println("+" + pad + "+");

+    }

+

+    public static void printBorder() {

+        StringBuilder pad = new StringBuilder();

+        for (int i = 0; i < WIDTH; i ++) {

+            pad.append("=");

+        }

+        System.out.println("+" + pad + "+");

+    }

+   

+    public static void printBody(String msg) {

+        StringBuilder pad = new StringBuilder();

+        int len = WIDTH - msg.length() - 1;

+        if (len > 0) {

+            for (int i = 0; i < len; i ++) {

+                pad.append(" ");

+            }

+        }

+        System.out.println("| " + msg + pad + "|");

+    }

+    

+    public static void printHeader(String msg) {

+        StringBuilder pad = new StringBuilder();

+        int len = WIDTH - msg.length();

+        if (len > 0) {

+            int half = len / 2;

+            for (int i = 0; i < half; i ++) {

+                pad.append(" ");

+            }

+        }

+        System.out.println("|" + pad + msg + pad + ((len % 2 == 0) ? "" : " ") + "|");

+    }

+    

+    public static NetworkInterface getNetworkInterface() {

+        try {

+            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();

+            if (interfaces != null) {

+                while (interfaces.hasMoreElements()) {

+                    try {

+                        return interfaces.nextElement();

+                    } catch (Throwable e) {

+                    }

+                }

+            }

+        } catch (SocketException e) {

+        }

+        return null;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/RegistryTestSupport.java b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/RegistryTestSupport.java
new file mode 100644
index 0000000..d5ae6c0
--- /dev/null
+++ b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/RegistryTestSupport.java
@@ -0,0 +1,215 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.registry;
+
+
+/**
+ * @author ding.lid
+ */
+public class RegistryTestSupport {
+    /*public static <T> void assertEqualsIgnoreOrde(Collection<T> expected, Collection<T> actual) {
+        Set<T> expectedSet;
+        if (expected instanceof Set) {
+            expectedSet = (Set<T>) expected;
+        } else {
+            expectedSet = new HashSet<T>(expected);
+        }
+
+        Set<T> actualSet;
+        if (actual instanceof Set) {
+            actualSet = (Set<T>) actual;
+        } else {
+            actualSet = new HashSet<T>(actual);
+        }
+
+        Assert.assertEquals(expectedSet, actualSet);
+    }
+
+    public static final String              member_service_name = "com.alibaba.morgan.MemberService";
+
+    public static final Map<String, String> member_urls1;
+    static {
+        Map<String, String> m = new HashMap<String, String>();
+        m.put("remote://10.20.130.230:9090/memberService", "version=1.0.0");
+
+        member_urls1 = Collections.unmodifiableMap(m);
+    }
+
+    public static final Map<String, String> member_urls2;
+    static {
+        Map<String, String> m = new HashMap<String, String>();
+        m.put("remote://11.11.11.11:9090/memberService", "version=1.0.0");
+        m.put("remote://22.22.22.22:9090/memberService", "version=1.0.0");
+        m.put("remote://33.33.33.33:9090/memberService", "version=1.0.0");
+
+        member_urls2 = Collections.unmodifiableMap(m);
+    }
+
+    public static final Map<String, String> member_urls3;
+    static {
+        Map<String, String> m = new HashMap<String, String>();
+        m.put("remote://44.44.44.44:9090/memberService", "version=1.0.0");
+        member_urls3 = Collections.unmodifiableMap(m);
+    }
+
+    public static final String              xxx_service_name    = "com.alibaba.xxx.XxxService";
+
+    public static final Map<String, String> xxx_urls1;
+    static {
+        Map<String, String> m = new HashMap<String, String>();
+        m.put("remote://11.11.11.11:9090/xxxService", "version=1.0.0");
+        m.put("remote://22.22.22.22:9090/xxxService", "version=1.0.0");
+
+        xxx_urls1 = Collections.unmodifiableMap(m);
+    }
+
+    public static void registerCheck(AbstractRegistry registry) {
+        {
+            List<URL> registeredList = registry
+                    .getRegistered("com.alibaba.morgan.MemberService");
+            assertEquals(0, registeredList.size());
+
+            Map<String, Map<String, String>> registeredMap = registry.getRegistered();
+            assertEquals(0, registeredMap.size());
+        }
+
+        {
+            for (Map.Entry<String, String> entry : member_urls1.entrySet()) {
+                registry.register(member_service_name, entry.getKey(), entry.getValue());
+            }
+
+            Map<String, String> registered = registry.getRegistered(member_service_name);
+            assertEquals(1, registered.size());
+            assertNotSame(member_urls1, registered);
+            assertEquals(member_urls1, registered);
+
+            Map<String, Map<String, String>> registeredMap = registry.getRegistered();
+            assertEquals(1, registeredMap.size());
+            assertTrue(registeredMap.containsKey(member_service_name));
+
+            registered = registeredMap.get(member_service_name);
+            assertEquals(1, registered.size());
+            assertNotSame(member_urls1, registered);
+            assertEquals(member_urls1, registered);
+        }
+
+        {
+            registry.register(member_service_name, member_urls2);
+
+            Map<String, String> registered = registry.getRegistered(member_service_name);
+
+            final Map<String, String> urls = new HashMap<String, String>();
+            urls.putAll(member_urls1);
+            urls.putAll(member_urls2);
+
+            assertEquals(urls, registered);
+
+            Map<String, Map<String, String>> registeredMap = registry.getRegistered();
+            assertEquals(1, registeredMap.size());
+            assertTrue(registeredMap.containsKey(member_service_name));
+
+            registered = registeredMap.get(member_service_name);
+            assertEquals(urls, registered);
+        }
+
+        {
+            Map<String, Map<String, String>> services = new HashMap<String, Map<String, String>>();
+            services.put(member_service_name, member_urls3);
+            services.put(xxx_service_name, xxx_urls1);
+
+            registry.register(services);
+
+            final Map<String, String> urls = new HashMap<String, String>();
+            urls.putAll(member_urls1);
+            urls.putAll(member_urls2);
+            urls.putAll(member_urls3);
+            {
+                Map<String, String> registered = registry.getRegistered(member_service_name);
+                assertEquals(urls, registered);
+
+                registered = registry.getRegistered(xxx_service_name);
+                assertEquals(xxx_urls1, registered);
+            }
+
+            {
+                Map<String, Map<String, String>> registeredMap = registry.getRegistered();
+                assertEquals(2, registeredMap.size());
+                assertTrue(registeredMap.containsKey(member_service_name));
+                assertTrue(registeredMap.containsKey(xxx_service_name));
+
+                Map<String, String> registered = registeredMap.get(member_service_name);
+                assertEquals(urls, registered);
+
+                registered = registeredMap.get(xxx_service_name);
+                assertEquals(xxx_urls1, registered);
+            }
+        }
+    }
+
+    public static void subscribeCheck(AbstractRegistry registry, NotificationListener mockNotifyListener) {
+        String subscribed = registry.getSubscribed("com.alibaba.morgan.MemberService");
+        assertNull(subscribed);
+
+        Map<String, String> subscribedMap = registry.getSubscribed();
+        assertEquals(0, subscribedMap.size());
+
+        // query允许为null
+        registry.subscribe("com.alibaba.xxx.XxxService", (String) null, mockNotifyListener);
+
+        subscribed = registry.getSubscribed("com.alibaba.xxx.XxxService");
+        assertEquals("", subscribed);
+
+        subscribedMap = registry.getSubscribed();
+        assertEquals(1, subscribedMap.size());
+        assertTrue(subscribedMap.containsKey("com.alibaba.xxx.XxxService"));
+        assertEquals("", subscribedMap.get("com.alibaba.xxx.XxxService"));
+
+        registry.subscribe("com.alibaba.empty.EmptyService", "", mockNotifyListener);
+
+        // query允许为空
+        subscribed = registry.getSubscribed("com.alibaba.empty.EmptyService");
+        assertEquals("", subscribed);
+
+        subscribedMap = registry.getSubscribed();
+        assertEquals(2, subscribedMap.size());
+        assertTrue(subscribedMap.containsKey("com.alibaba.empty.EmptyService"));
+        assertEquals("", subscribedMap.get("com.alibaba.empty.EmptyService"));
+
+        // 多项值的Query
+        registry.subscribe("com.alibaba.morgan.MemberService", "dog=bad,cat=god",
+                mockNotifyListener);
+
+        subscribed = registry.getSubscribed("com.alibaba.morgan.MemberService");
+        assertEquals("dog=bad,cat=god", subscribed);
+
+        subscribedMap = registry.getSubscribed();
+        assertEquals(3, subscribedMap.size());
+        String s = subscribedMap.get("com.alibaba.morgan.MemberService");
+        assertEquals("dog=bad,cat=god", s);
+
+        registry.subscribe("com.alibaba.complex.ComplexService",
+                "version=1.0.0&application=kylin&methods=findPerson,findVAccount",
+                mockNotifyListener);
+
+        subscribedMap = registry.getSubscribed();
+        assertEquals(4, subscribedMap.size());
+        String q = subscribedMap.get("com.alibaba.complex.ComplexService");
+        assertEquals("version=1.0.0&application=kylin&methods=findPerson,findVAccount", q);
+    }*/
+    
+    public void testDummy() {
+    }
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/test/resources/log4j.xml b/dubbo-registry/src/test/resources/log4j.xml
new file mode 100644
index 0000000..1eb50f0
--- /dev/null
+++ b/dubbo-registry/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+	<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %5p %c{2}: %m%n" />
+		</layout>
+	</appender>
+	<root>
+		<level value="WARN" />
+		<appender-ref ref="CONSOLE" />
+	</root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/pom.xml b/dubbo-remoting-grizzly/pom.xml
new file mode 100644
index 0000000..c569d2d
--- /dev/null
+++ b/dubbo-remoting-grizzly/pom.xml
@@ -0,0 +1,42 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-remoting-grizzly</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Grizzly Remoting Module</name>
+	<description>The grizzly remoting module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.glassfish.grizzly</groupId>
+			<artifactId>grizzly-core</artifactId>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyChannel.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyChannel.java
new file mode 100644
index 0000000..65df0cf
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyChannel.java
@@ -0,0 +1,178 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.grizzly;

+

+import java.net.InetSocketAddress;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.TimeoutException;

+

+import org.glassfish.grizzly.Connection;

+import org.glassfish.grizzly.Grizzly;

+import org.glassfish.grizzly.GrizzlyFuture;

+import org.glassfish.grizzly.attributes.Attribute;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.transport.AbstractChannel;

+

+/**

+ * GrizzlyChannel

+ * 

+ * @author william.liangf

+ */

+final class GrizzlyChannel extends AbstractChannel {

+

+    private static final Logger logger = LoggerFactory.getLogger(GrizzlyChannel.class);

+

+    private static final String CHANNEL_KEY = GrizzlyChannel.class.getName() + ".CHANNEL";

+    

+    private static final Attribute<GrizzlyChannel> ATTRIBUTE = Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(CHANNEL_KEY);

+

+    private final Connection<?> connection;

+

+    /**

+     * @param connection

+     * @param url

+     * @param handler

+     */

+    private GrizzlyChannel(Connection<?> connection, URL url, ChannelHandler handler){

+        super(url, handler);

+        if (connection == null) {

+            throw new IllegalArgumentException("grizzly connection == null");

+        }

+        this.connection = connection;

+    }

+

+    static GrizzlyChannel getOrAddChannel(Connection<?> connection, URL url, ChannelHandler handler) {

+        if (connection == null) {

+            return null;

+        }

+        GrizzlyChannel ret = ATTRIBUTE.get(connection);

+        if (ret == null) {

+            ret = new GrizzlyChannel(connection, url, handler);

+            if (connection.isOpen()) {

+                ATTRIBUTE.set(connection, ret);

+            }

+        }

+        return ret;

+    }

+

+    static void removeChannelIfDisconnectd(Connection<?> connection) {

+        if (connection != null && ! connection.isOpen()) {

+            ATTRIBUTE.remove(connection);

+        }

+    }

+

+    public InetSocketAddress getRemoteAddress() {

+        return (InetSocketAddress) connection.getPeerAddress();

+    }

+

+    public boolean isConnected() {

+        return connection.isOpen();

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return (InetSocketAddress) connection.getLocalAddress();

+    }

+

+    @SuppressWarnings("rawtypes")

+    public void send(Object message, boolean sent) throws RemotingException {

+        super.send(message, sent);

+        

+        int timeout = 0;

+        try {

+            GrizzlyFuture future = connection.write(message);

+            if (sent) {

+                timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);

+                future.get(timeout, TimeUnit.MILLISECONDS);

+            }

+        }

+        catch (TimeoutException e) {

+            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()

+                    + "in timeout(" + timeout + "ms) limit", e);

+        }

+        catch (Throwable e) {

+            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);

+        }

+    }

+

+    public void close() {

+        try {

+            super.close();

+        } catch (Exception e) {

+            logger.warn(e.getMessage(), e);

+        }

+        try {

+            removeChannelIfDisconnectd(connection);

+        } catch (Exception e) {

+            logger.warn(e.getMessage(), e);

+        }

+        try {

+            if (logger.isInfoEnabled()) {

+                logger.info("Close grizzly channel " + connection);

+            }

+            connection.close();

+        } catch (Exception e) {

+            logger.warn(e.getMessage(), e);

+        }

+    }

+

+    public boolean hasAttribute(String key) {

+        return getAttribute(key) == null;

+    }

+

+    public Object getAttribute(String key) {

+        return Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(key).get(connection);

+    }

+

+    public void setAttribute(String key, Object value) {

+        Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(key).set(connection, value);

+    }

+

+    public void removeAttribute(String key) {

+        Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(key).remove(connection);

+    }

+

+    @Override

+    public int hashCode() {

+        final int prime = 31;

+        int result = 1;

+        result = prime * result + ((connection == null) ? 0 : connection.hashCode());

+        return result;

+    }

+

+    @Override

+    public boolean equals(Object obj) {

+        if (this == obj) return true;

+        if (obj == null) return false;

+        if (getClass() != obj.getClass()) return false;

+        GrizzlyChannel other = (GrizzlyChannel) obj;

+        if (connection == null) {

+            if (other.connection != null) return false;

+        } else if (!connection.equals(other.connection)) return false;

+        return true;

+    }

+

+    @Override

+    public String toString() {

+        return "GrizzlyChannel [connection=" + connection + "]";

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyClient.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyClient.java
new file mode 100644
index 0000000..34a6050
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyClient.java
@@ -0,0 +1,109 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.grizzly;

+

+import java.util.concurrent.TimeUnit;

+

+import org.glassfish.grizzly.Connection;

+import org.glassfish.grizzly.filterchain.FilterChainBuilder;

+import org.glassfish.grizzly.filterchain.TransportFilter;

+import org.glassfish.grizzly.nio.transport.TCPNIOTransport;

+import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder;

+import org.glassfish.grizzly.strategies.SameThreadIOStrategy;

+import org.glassfish.grizzly.threadpool.ThreadPoolConfig;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.transport.AbstractClient;

+

+/**

+ * GrizzlyClient

+ * 

+ * @author william.liangf

+ */

+public class GrizzlyClient extends AbstractClient {

+    

+    private static final Logger logger = LoggerFactory.getLogger(GrizzlyClient.class);

+

+    private TCPNIOTransport transport;

+

+    private volatile Connection<?> connection; // volatile, please copy reference to use

+

+    public GrizzlyClient(URL url, ChannelHandler handler) throws RemotingException {

+        super(url, handler);

+    }

+

+    @Override

+    protected void doOpen() throws Throwable {

+        FilterChainBuilder filterChainBuilder = FilterChainBuilder.stateless();

+        filterChainBuilder.add(new TransportFilter());

+        filterChainBuilder.add(new GrizzlyCodecAdapter(getCodec(), getUrl(), this));

+        filterChainBuilder.add(new GrizzlyHandler(getUrl(), this));

+        TCPNIOTransportBuilder builder = TCPNIOTransportBuilder.newInstance();

+        ThreadPoolConfig config = builder.getWorkerThreadPoolConfig(); 

+        config.setPoolName(CLIENT_THREAD_POOL_NAME)

+                .setQueueLimit(-1)

+                .setCorePoolSize(0)

+                .setMaxPoolSize(Integer.MAX_VALUE)

+                .setKeepAliveTime(60L, TimeUnit.SECONDS);

+        builder.setTcpNoDelay(true).setKeepAlive(true)

+                .setConnectionTimeout(getTimeout())

+                .setIOStrategy(SameThreadIOStrategy.getInstance());

+        transport = builder.build();

+        transport.setProcessor(filterChainBuilder.build());

+        transport.start();

+    }

+

+    

+

+    @Override

+    protected void doConnect() throws Throwable {

+        connection = transport.connect(getConnectAddress())

+                        .get(getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS);

+    }

+

+    @Override

+    protected void doDisConnect() throws Throwable {

+        try {

+            GrizzlyChannel.removeChannelIfDisconnectd(connection);

+        } catch (Throwable t) {

+            logger.warn(t.getMessage());

+        }

+    }

+

+    @Override

+    protected void doClose() throws Throwable {

+        try {

+            transport.stop();

+        } catch (Throwable e) {

+            logger.warn(e.getMessage(), e);

+        }

+    }

+    

+    @Override

+    protected Channel getChannel() {

+        Connection<?> c = connection;

+        if (c == null || ! c.isOpen())

+            return null;

+        return GrizzlyChannel.getOrAddChannel(c, getUrl(), this);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java
new file mode 100644
index 0000000..2d235ab
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java
@@ -0,0 +1,189 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.grizzly;

+

+import java.io.IOException;

+

+import org.glassfish.grizzly.Buffer;

+import org.glassfish.grizzly.Connection;

+import org.glassfish.grizzly.filterchain.BaseFilter;

+import org.glassfish.grizzly.filterchain.FilterChainContext;

+import org.glassfish.grizzly.filterchain.NextAction;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.io.Bytes;

+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;

+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Codec;

+import com.alibaba.dubbo.remoting.exchange.Response;

+

+/**

+ * GrizzlyCodecAdapter

+ * 

+ * @author william.liangf

+ */

+public class GrizzlyCodecAdapter extends BaseFilter {

+

+    private static final String   BUFFER_KEY = GrizzlyCodecAdapter.class.getName() + ".BUFFER";

+

+    private final Codec           upstreamCodec;

+    private final Codec           downstreamCodec;

+

+    private final URL             url;

+    

+    private final ChannelHandler  handler;

+

+    private final int             bufferSize;

+    

+    public GrizzlyCodecAdapter(Codec codec, URL url, ChannelHandler handler){

+        this(codec, codec, url, handler);

+    }

+    /**

+     * server 端如果有消息发送需要分开codec,默认的上行code是dubbo1兼容的

+     */

+    public GrizzlyCodecAdapter(Codec upstreamCodec, Codec downstreamCodec, URL url, ChannelHandler handler){

+        this.upstreamCodec = upstreamCodec;

+        this.downstreamCodec = downstreamCodec;

+        this.url = url;

+        this.handler = handler;

+        int b = url.getPositiveParameter(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);

+        this.bufferSize = b >= Constants.MIN_BUFFER_SIZE && b <= Constants.MAX_BUFFER_SIZE ? b : Constants.DEFAULT_BUFFER_SIZE;

+    }

+

+    @Override

+    public NextAction handleWrite(FilterChainContext context) throws IOException {

+        Connection<?> connection = context.getConnection();

+        GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);

+        try {

+            UnsafeByteArrayOutputStream output = new UnsafeByteArrayOutputStream(1024); // 不需要关闭

+            

+            if(!(context.getMessage() instanceof Response)){

+                downstreamCodec.encode(channel, output, context.getMessage());

+            }else{

+                upstreamCodec.encode(channel, output, context.getMessage());

+            }

+            

+            GrizzlyChannel.removeChannelIfDisconnectd(connection);

+            byte[] bytes = output.toByteArray();

+            Buffer buffer = connection.getTransport().getMemoryManager().allocate(bytes.length);

+            buffer.put(bytes);

+            buffer.flip();

+            buffer.allowBufferDispose(true);

+            context.setMessage(buffer);

+        } finally {

+            GrizzlyChannel.removeChannelIfDisconnectd(connection);

+        }

+        return context.getInvokeAction();

+    }

+

+    @Override

+    public NextAction handleRead(FilterChainContext context) throws IOException {

+        Object message = context.getMessage();

+        Connection<?> connection = context.getConnection();

+        Channel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);

+        try {

+            if (message instanceof Buffer) { // 收到新的数据包

+                Buffer buffer = (Buffer) message; // 缓存

+                int readable = buffer.capacity(); // 本次可读取新数据的大小

+                if (readable == 0) {

+                    return context.getStopAction();

+                }

+                byte[] bytes; // byte[]缓存区,将Buffer转成byte[],再转成UnsafeByteArrayInputStream

+                int offset; // 指向已用数据的偏移量,off之前的数据都是已用过的

+                int limit; // 有效长度,limit之后的长度是空白或无效数据,off到limit之间的数据是准备使用的有效数据

+                Object[] remainder = (Object[]) channel.getAttribute(BUFFER_KEY); // 上次序列化剩下的数据

+                channel.removeAttribute(BUFFER_KEY);

+                if (remainder == null) { // 如果没有,创建新的bytes缓存

+                    bytes = new byte[bufferSize];

+                    offset = 0;

+                    limit = 0;

+                } else { // 如果有,使用剩下的bytes缓存

+                    bytes = (byte[]) remainder[0];

+                    offset = (Integer) remainder[1];

+                    limit = (Integer) remainder[2];

+                }

+                return receive(context, channel, buffer, readable, bytes, offset, limit);

+            } else if (message instanceof Object[]) { // 同一Buffer多轮Filter,即:一个Buffer里有多个请求

+                Object[] remainder = (Object[]) message;

+                Buffer buffer = (Buffer) remainder[0];

+                int readable = (Integer) remainder[1];

+                byte[] bytes = (byte[]) remainder[2];

+                int offset = (Integer) remainder[3];

+                int limit = (Integer) remainder[4];

+                return receive(context, channel, buffer, readable, bytes, offset, limit);

+            } else { // 其它事件直接往下传

+                return context.getInvokeAction();

+            }

+        } finally {

+            GrizzlyChannel.removeChannelIfDisconnectd(connection);

+        }

+    }

+    

+    /*

+     * 接收

+     * 

+     * @param context 上下文

+     * @param channel 通道

+     * @param buffer 缓存

+     * @param readable 缓存可读

+     * @param bytes 输入缓存

+     * @param offset 指向已读数据的偏移量,off之前的数据都是已用过的

+     * @param limit 有效长度,limit之后的长度是空白或无效数据,off到limit之间的数据是准备使用的数据

+     * @return 后续动作

+     * @throws IOException

+     */

+    private NextAction receive(FilterChainContext context, Channel channel, Buffer buffer, int readable, byte[] bytes, int offset, int limit) throws IOException {

+        for(;;) {

+            int read = Math.min(readable, bytes.length - limit); // 取bytes缓存空闲区,和可读取新数据,的最小值,即:此次最多读写数据的大小

+            buffer.get(bytes, limit, read); // 从可读取新数据中,读取数据,尽量填满bytes缓存空闲区

+            limit += read; // 有效数据变长

+            readable -= read; // 可读数据变少

+            UnsafeByteArrayInputStream input = new UnsafeByteArrayInputStream(bytes, offset, limit - offset); // 将bytes缓存转成InputStream,不需要关闭

+            Object msg = upstreamCodec.decode(channel, input); // 调用Codec接口,解码数据

+            if (msg == Codec.NEED_MORE_INPUT) { // 如果Codec觉得数据不够,不足以解码成一个对象

+                if (readable == 0) { // 如果没有更多可读数据

+                    channel.setAttribute(BUFFER_KEY, new Object[] { bytes, offset, limit }); // 放入通道属性中,等待下一个Buffer的到来

+                    return context.getStopAction();

+                } else { // 扩充或挪出空闲区,并循环,直到可读数据都加载到bytes缓存

+                    if (offset == 0) { // 如果bytes缓存全部没有被使用,如果这时数据还不够

+                        bytes = Bytes.copyOf(bytes, bytes.length << 1); // 将bytes缓存扩大一倍

+                    } else { // 如果bytes缓存有一段数据已被使用

+                        int len = limit - offset; // 计算有效数据长度

+                        System.arraycopy(bytes, offset, bytes, 0, len); // 将数据向前移到,压缩到已使用的部分,这样limit后面就会多出一些空闲,可以放数据

+                        offset = 0; // 移到后,bytes缓存没有数据被使用

+                        limit = len; // 移到后,有效数据都在bytes缓存最前面

+                    }

+                }

+            } else { // 如果解析出一个结果

+                int position = input.position(); // 记录InputStream用了多少

+                if (position == offset) { // 如果InputStream没有被读过,就返回了数据,直接报错,否则InputStream永远读不完,会死递归

+                    throw new IOException("Decode without read data.");

+                }

+                offset = position; // 记录已读数据

+                context.setMessage(msg); // 将消息改为解码后的对象,以便被后面的Filter使用。

+                if (limit - offset > 0 || readable > 0) { // 如果有效数据没有被读完,或者Buffer区还有未读数据

+                    return context.getInvokeAction(new Object[] { buffer, readable, bytes, offset, limit }); // 正常执行完Filter,并重新发起一轮Filter,继续读

+                } else { // 否则所有数据读完

+                    return context.getInvokeAction(); // 正常执行完Filter

+                }

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyHandler.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyHandler.java
new file mode 100644
index 0000000..f7196cd
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyHandler.java
@@ -0,0 +1,119 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.grizzly;

+

+import java.io.IOException;

+

+import org.glassfish.grizzly.Connection;

+import org.glassfish.grizzly.filterchain.BaseFilter;

+import org.glassfish.grizzly.filterchain.FilterChainContext;

+import org.glassfish.grizzly.filterchain.NextAction;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * GrizzlyHandler

+ * 

+ * @author william.liangf

+ */

+public class GrizzlyHandler extends BaseFilter {

+

+    private static final Logger logger = LoggerFactory.getLogger(GrizzlyHandler.class);

+

+    private final URL url;

+    

+    private final ChannelHandler handler;

+    

+    public GrizzlyHandler(URL url, ChannelHandler handler){

+        this.url = url;

+        this.handler = handler;

+    }

+

+    @Override

+    public NextAction handleConnect(FilterChainContext ctx) throws IOException {

+        Connection<?> connection = ctx.getConnection();

+        GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);

+        try {

+            handler.connected(channel);

+        } catch (RemotingException e) {

+            throw new IOException(StringUtils.toString(e));

+        } finally {

+            GrizzlyChannel.removeChannelIfDisconnectd(connection);

+        }

+        return ctx.getInvokeAction();

+    }

+    

+    @Override

+    public NextAction handleClose(FilterChainContext ctx) throws IOException {

+        Connection<?> connection = ctx.getConnection();

+        GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);

+        try {

+            handler.disconnected(channel);

+        } catch (RemotingException e) {

+            throw new IOException(StringUtils.toString(e));

+        } finally {

+            GrizzlyChannel.removeChannelIfDisconnectd(connection);

+        }

+        return ctx.getInvokeAction();

+    }

+

+    @Override

+    public NextAction handleRead(FilterChainContext ctx) throws IOException {

+        Connection<?> connection = ctx.getConnection();

+        GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);

+        try {

+            handler.received(channel, ctx.getMessage());

+        } catch (RemotingException e) {

+            throw new IOException(StringUtils.toString(e));

+        } finally {

+            GrizzlyChannel.removeChannelIfDisconnectd(connection);

+        }

+        return ctx.getInvokeAction();

+    }

+

+    @Override

+    public NextAction handleWrite(FilterChainContext ctx) throws IOException {

+        Connection<?> connection = ctx.getConnection();

+        GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);

+        try {

+            handler.sent(channel, ctx.getMessage());

+        } catch (RemotingException e) {

+            throw new IOException(StringUtils.toString(e));

+        } finally {

+            GrizzlyChannel.removeChannelIfDisconnectd(connection);

+        }

+        return ctx.getInvokeAction();

+    }

+    

+    @Override

+    public void exceptionOccurred(FilterChainContext ctx, Throwable error) {

+        Connection<?> connection = ctx.getConnection();

+        GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);

+        try {

+            handler.caught(channel, error);

+        } catch (RemotingException e) {

+            logger.error("RemotingException on channel " + channel, e);

+        } finally {

+            GrizzlyChannel.removeChannelIfDisconnectd(connection);

+        }

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyServer.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyServer.java
new file mode 100644
index 0000000..722ae67
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyServer.java
@@ -0,0 +1,121 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.grizzly;

+

+import java.net.InetSocketAddress;

+import java.util.Collection;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.TimeUnit;

+

+import org.glassfish.grizzly.filterchain.FilterChainBuilder;

+import org.glassfish.grizzly.filterchain.TransportFilter;

+import org.glassfish.grizzly.nio.transport.TCPNIOTransport;

+import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder;

+import org.glassfish.grizzly.strategies.SameThreadIOStrategy;

+import org.glassfish.grizzly.threadpool.ThreadPoolConfig;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.transport.AbstractServer;

+

+/**

+ * GrizzlyServer

+ * 

+ * @author william.liangf

+ */

+public class GrizzlyServer extends AbstractServer {

+    

+    private static final Logger logger = LoggerFactory.getLogger(GrizzlyServer.class);

+

+    private final Map<String, Channel> channels = new ConcurrentHashMap<String, Channel>(); // <ip:port, channel>

+    

+    private TCPNIOTransport transport;

+

+    public GrizzlyServer(URL url, ChannelHandler handler) throws RemotingException {

+        super(url, handler);

+    }

+

+    @Override

+    protected void doOpen() throws Throwable {

+        FilterChainBuilder filterChainBuilder = FilterChainBuilder.stateless();

+        filterChainBuilder.add(new TransportFilter());

+        

+        filterChainBuilder.add(new GrizzlyCodecAdapter(getCodec(), getDownstreamCodec(), getUrl(), this));

+        filterChainBuilder.add(new GrizzlyHandler(getUrl(), this));

+        TCPNIOTransportBuilder builder = TCPNIOTransportBuilder.newInstance();

+        ThreadPoolConfig config = builder.getWorkerThreadPoolConfig(); 

+        config.setPoolName(SERVER_THREAD_POOL_NAME).setQueueLimit(-1);

+        String threadpool = getUrl().getParameter(Constants.THREADPOOL_KEY, Constants.DEFAULT_THREADPOOL);

+        if (Constants.DEFAULT_THREADPOOL.equals(threadpool)) {

+            int threads = getUrl().getPositiveParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);

+            config.setCorePoolSize(threads).setMaxPoolSize(threads)

+                .setKeepAliveTime(0L, TimeUnit.SECONDS); 

+        } else if ("cached".equals(threadpool)) {

+            int threads = getUrl().getPositiveParameter(Constants.THREADS_KEY, Integer.MAX_VALUE);

+            config.setCorePoolSize(0).setMaxPoolSize(threads)

+                .setKeepAliveTime(60L, TimeUnit.SECONDS);

+        } else {

+            throw new IllegalArgumentException("Unsupported threadpool type " + threadpool);

+        }

+        builder.setKeepAlive(true).setReuseAddress(false)

+                .setIOStrategy(SameThreadIOStrategy.getInstance());

+        transport = builder.build();

+        transport.setProcessor(filterChainBuilder.build());

+        transport.bind(getBindAddress());

+        transport.start();

+    }

+

+    @Override

+    protected void doClose() throws Throwable {

+        try {

+            transport.stop();

+        } catch (Throwable e) {

+            logger.warn(e.getMessage(), e);

+        }

+    }

+

+    public boolean isBound() {

+        return ! transport.isStopped();

+    }

+

+    public Collection<Channel> getChannels() {

+        return channels.values();

+    }

+

+    public Channel getChannel(InetSocketAddress remoteAddress) {

+        return channels.get(NetUtils.toAddressString(remoteAddress));

+    }

+

+    @Override

+    public void connected(Channel ch) throws RemotingException {

+        channels.put(NetUtils.toAddressString(ch.getRemoteAddress()), ch);

+        super.connected(ch);

+    }

+

+    @Override

+    public void disconnected(Channel ch) throws RemotingException {

+        channels.remove(NetUtils.toAddressString(ch.getRemoteAddress()));

+        super.disconnected(ch);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.java
new file mode 100644
index 0000000..5f66322
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.java
@@ -0,0 +1,42 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.grizzly;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.Transporter;

+

+/**

+ * GrizzlyTransporter

+ * 

+ * @author william.liangf

+ */

+public class GrizzlyTransporter implements Transporter {

+

+    public static final String NAME = "grizzly";

+

+    public Server bind(URL url, ChannelHandler listener) throws RemotingException {

+        return new GrizzlyServer(url, listener);

+    }

+

+    public Client connect(URL url, ChannelHandler listener) throws RemotingException {

+        return new GrizzlyClient(url, listener);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting-grizzly/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..1cd2509
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+grizzly=com.alibaba.dubbo.remoting.transport.grizzly.GrizzlyTransporter
\ No newline at end of file
diff --git a/dubbo-remoting-http/pom.xml b/dubbo-remoting-http/pom.xml
new file mode 100644
index 0000000..57e6cbb
--- /dev/null
+++ b/dubbo-remoting-http/pom.xml
@@ -0,0 +1,42 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-remoting-http</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Http Remoting Module</name>
+	<description>The http remoting module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-common</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.mortbay.jetty</groupId>
+			<artifactId>jetty</artifactId>
+		</dependency>

+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java
new file mode 100644
index 0000000..124ff26
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.http;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * HttpBinder

+ * 

+ * @author william.liangf

+ */

+@SPI("jetty")

+public interface HttpBinder {

+    

+    /**

+     * bind the server.

+     * 

+     * @param url server url.

+     * @return server.

+     */

+    @Adaptive({Constants.SERVER_KEY})

+    HttpServer bind(URL url, HttpHandler handler);

+    

+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpHandler.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpHandler.java
new file mode 100644
index 0000000..6550a3b
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpHandler.java
@@ -0,0 +1,41 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.http;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * http invocation handler.
+ * 
+ * @author william.liangf
+ */
+public interface HttpHandler {
+    
+    /**
+	 * invoke.
+	 * 
+	 * @param request request.
+	 * @param response response.
+	 * @throws IOException
+	 * @throws ServletException
+	 */
+    void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
+    
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpServer.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpServer.java
new file mode 100644
index 0000000..1aa44ed
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpServer.java
@@ -0,0 +1,70 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.http;

+

+import java.net.InetSocketAddress;

+

+import com.alibaba.dubbo.common.Resetable;

+import com.alibaba.dubbo.common.URL;

+

+public interface HttpServer extends Resetable {

+    

+    /**

+     * get http handler.

+     * 

+     * @return http handler.

+     */

+    HttpHandler getHttpHandler();

+    

+    /**

+     * get url.

+     * 

+     * @return url

+     */

+    URL getUrl();

+    

+    /**

+     * get local address.

+     * 

+     * @return local address.

+     */

+    InetSocketAddress getLocalAddress();

+    

+    /**

+     * close the channel.

+     */

+    void close();

+    

+    /**

+     * Graceful close the channel.

+     */

+    void close(int timeout);

+    

+    /**

+     * is bound.

+     * 

+     * @return bound

+     */

+    boolean isBound();

+    

+    /**

+     * is closed.

+     * 

+     * @return closed

+     */

+    boolean isClosed();

+    

+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.java
new file mode 100644
index 0000000..4741b90
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.http.jetty;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.http.HttpBinder;

+import com.alibaba.dubbo.remoting.http.HttpHandler;

+import com.alibaba.dubbo.remoting.http.HttpServer;

+

+/**

+ * JettyHttpTransporter

+ * 

+ * @author william.liangf

+ */

+public class JettyHttpBinder implements HttpBinder {

+

+    public HttpServer bind(URL url, HttpHandler handler) {

+        return new JettyHttpServer(url, handler);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java
new file mode 100644
index 0000000..cb30752
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java
@@ -0,0 +1,88 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.http.jetty;

+

+import java.io.IOException;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import org.mortbay.jetty.Server;

+import org.mortbay.jetty.handler.AbstractHandler;

+import org.mortbay.jetty.nio.SelectChannelConnector;

+import org.mortbay.thread.QueuedThreadPool;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.http.HttpHandler;

+import com.alibaba.dubbo.remoting.http.support.AbstractHttpServer;

+

+public class JettyHttpServer extends AbstractHttpServer {

+

+    private static final Logger logger = LoggerFactory.getLogger(JettyHttpServer.class);

+

+    private Server              server;

+

+    public JettyHttpServer(URL url, final HttpHandler handler){

+        super(url, handler);

+

+        int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);

+        QueuedThreadPool threadPool = new QueuedThreadPool();

+        threadPool.setDaemon(true);

+        threadPool.setMaxThreads(threads);

+        threadPool.setMinThreads(threads);

+

+        SelectChannelConnector connector = new SelectChannelConnector();

+        if (NetUtils.isValidLocalHost(url.getHost())) {

+            connector.setHost(url.getHost());

+        }

+        connector.setPort(url.getPort());

+

+        server = new Server();

+        server.setThreadPool(threadPool);

+        server.addConnector(connector);

+        server.addHandler(new AbstractHandler() {

+            public void handle(String target, HttpServletRequest request,

+                               HttpServletResponse response, int dispatch) throws IOException,

+                    ServletException {

+                handler.handle(request, response);

+            }

+        });

+        

+        try {

+            server.start();

+        } catch (Exception e) {

+            throw new IllegalStateException("Failed to start jetty server on " + url.getAddress() + ", cause: "

+                                            + e.getMessage(), e);

+        }

+    }

+

+    public void close() {

+        super.close();

+        if (server != null) {

+            try {

+                server.stop();

+            } catch (Exception e) {

+                logger.warn(e.getMessage(), e);

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/DispatcherServlet.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/DispatcherServlet.java
new file mode 100644
index 0000000..29e7a34
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/DispatcherServlet.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.http.servlet;
+
+import java.io.IOException;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import com.alibaba.dubbo.remoting.http.HttpHandler;

+
+/**
+ * Service dispatcher Servlet.
+ * 
+ * @author qian.lei
+ */
+public class DispatcherServlet extends HttpServlet {
+
+	private static final long serialVersionUID = 5766349180380479888L;
+
+    private static final Map<Integer, HttpHandler> handlers = new ConcurrentHashMap<Integer, HttpHandler>();
+
+    static void addHttpInvoker(int port, HttpHandler processor) {
+        handlers.put(port, processor);
+    }
+
+    static void removeHttpInvoker(int port) {
+        handlers.remove(port);
+    }
+
+    protected void service(HttpServletRequest request, HttpServletResponse response) 
+    		throws ServletException, IOException {
+        HttpHandler handler = handlers.get(request.getLocalPort());
+        if( handler == null ) {// service not found.
+            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Service not found.");
+        } else {
+            handler.handle(request, response);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.java
new file mode 100644
index 0000000..71d71f8
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.http.servlet;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.remoting.http.HttpBinder;

+import com.alibaba.dubbo.remoting.http.HttpHandler;

+import com.alibaba.dubbo.remoting.http.HttpServer;

+

+/**

+ * ServletHttpTransporter

+ * 

+ * @author william.liangf

+ */

+public class ServletHttpBinder implements HttpBinder {

+    

+    @Adaptive()

+    public HttpServer bind(URL url, HttpHandler handler) {

+        return new ServletHttpServer(url, handler);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpServer.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpServer.java
new file mode 100644
index 0000000..0b9433a
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpServer.java
@@ -0,0 +1,29 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.http.servlet;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.http.HttpHandler;

+import com.alibaba.dubbo.remoting.http.support.AbstractHttpServer;

+

+public class ServletHttpServer extends AbstractHttpServer {

+    

+    public ServletHttpServer(URL url, HttpHandler handler){

+        super(url, handler);

+        DispatcherServlet.addHttpInvoker(url.getPort(), handler);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/support/AbstractHttpServer.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/support/AbstractHttpServer.java
new file mode 100644
index 0000000..295cb18
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/support/AbstractHttpServer.java
@@ -0,0 +1,79 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.http.support;

+

+import java.net.InetSocketAddress;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.http.HttpHandler;

+import com.alibaba.dubbo.remoting.http.HttpServer;

+

+/**

+ * AbstractHttpServer

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractHttpServer implements HttpServer {

+

+    private final URL url;

+    

+    private final HttpHandler handler;

+

+    private volatile boolean closed;

+    

+    public AbstractHttpServer(URL url, HttpHandler handler){

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        if (handler == null) {

+            throw new IllegalArgumentException("handler == null");

+        }

+        this.url = url;

+        this.handler = handler;

+    }

+    

+    public HttpHandler getHttpHandler() {

+        return handler;

+    }

+

+    public URL getUrl() {

+        return url;

+    }

+

+    public void reset(URL url) {

+    }

+    

+    public boolean isBound() {

+        return true;

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return url.toInetSocketAddress();

+    }

+

+    public void close() {

+        closed = true;

+    }

+

+    public void close(int timeout) {

+        close();

+    }

+

+    public boolean isClosed() {

+        return closed;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.http.HttpBinder b/dubbo-remoting-http/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.http.HttpBinder
new file mode 100644
index 0000000..b1be107
--- /dev/null
+++ b/dubbo-remoting-http/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.http.HttpBinder
@@ -0,0 +1,2 @@
+servlet=com.alibaba.dubbo.remoting.http.servlet.ServletHttpBinder

+jetty=com.alibaba.dubbo.remoting.http.jetty.JettyHttpBinder
\ No newline at end of file
diff --git a/dubbo-remoting-mina/pom.xml b/dubbo-remoting-mina/pom.xml
new file mode 100644
index 0000000..2f1b800
--- /dev/null
+++ b/dubbo-remoting-mina/pom.xml
@@ -0,0 +1,46 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-remoting-mina</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Mina Remoting Module</name>
+	<description>The mina remoting module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.mina</groupId>
+			<artifactId>mina-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaChannel.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaChannel.java
new file mode 100644
index 0000000..12fd662
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaChannel.java
@@ -0,0 +1,172 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.mina;
+
+import java.net.InetSocketAddress;
+
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.WriteFuture;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.AbstractChannel;
+
+/**
+ * MinaChannel
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ */
+final class MinaChannel extends AbstractChannel {
+
+    private static final Logger logger = LoggerFactory.getLogger(MinaChannel.class);
+
+    private static final String CHANNEL_KEY = MinaChannel.class.getName() + ".CHANNEL";
+
+    private final IoSession     session;
+
+    private MinaChannel(IoSession session, URL url, ChannelHandler handler){
+        super(url, handler);
+        if (session == null) {
+            throw new IllegalArgumentException("mina session == null");
+        }
+        this.session = session;
+    }
+
+    static MinaChannel getOrAddChannel(IoSession session, URL url, ChannelHandler handler) {
+        if (session == null) {
+            return null;
+        }
+        MinaChannel ret = (MinaChannel) session.getAttribute(CHANNEL_KEY);
+        if (ret == null) {
+            ret = new MinaChannel(session, url, handler);
+            if (session.isConnected()) {
+                MinaChannel old = (MinaChannel) session.setAttribute(CHANNEL_KEY, ret);
+                if (old != null) {
+                    session.setAttribute(CHANNEL_KEY, old);
+                    ret = old;
+                }
+            }
+        }
+        return ret;
+    }
+
+    static void removeChannelIfDisconnectd(IoSession session) {
+        if (session != null && ! session.isConnected()) {
+            session.removeAttribute(CHANNEL_KEY);
+        }
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        return (InetSocketAddress) session.getLocalAddress();
+    }
+
+    public InetSocketAddress getRemoteAddress() {
+        return (InetSocketAddress) session.getRemoteAddress();
+    }
+
+    public boolean isConnected() {
+        return session.isConnected();
+    }
+
+    public void send(Object message, boolean sent) throws RemotingException {
+        super.send(message, sent);
+        
+        boolean success = true;
+        int timeout = 0;
+        try {
+            WriteFuture future = session.write(message);
+            if (sent) {
+                timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+                success = future.join(timeout);
+            }
+        } catch (Throwable e) {
+            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);
+        }
+        
+        if(!success) {
+            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
+                    + "in timeout(" + timeout + "ms) limit");
+        }
+    }
+
+    public void close() {
+        try {
+            super.close();
+        } catch (Exception e) {
+            logger.warn(e.getMessage(), e);
+        }
+        try {
+            removeChannelIfDisconnectd(session);
+        } catch (Exception e) {
+            logger.warn(e.getMessage(), e);
+        }
+        try {
+            if (logger.isInfoEnabled()) {
+                logger.info("CLose mina channel " + session);
+            }
+            session.close();
+        } catch (Exception e) {
+            logger.warn(e.getMessage(), e);
+        }
+    }
+
+    public boolean hasAttribute(String key) {
+        return session.containsAttribute(key);
+    }
+
+    public Object getAttribute(String key) {
+        return session.getAttribute(key);
+    }
+
+    public void setAttribute(String key, Object value) {
+        session.setAttribute(key, value);
+    }
+
+    public void removeAttribute(String key) {
+        session.removeAttribute(key);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((session == null) ? 0 : session.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        MinaChannel other = (MinaChannel) obj;
+        if (session == null) {
+            if (other.session != null) return false;
+        } else if (!session.equals(other.session)) return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "MinaChannel [session=" + session + "]";
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaClient.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaClient.java
new file mode 100644
index 0000000..47cae52
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaClient.java
@@ -0,0 +1,174 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.mina;
+
+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.CountDownLatch;

+import java.util.concurrent.Executors;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.atomic.AtomicReference;

+

+import org.apache.mina.common.ConnectFuture;

+import org.apache.mina.common.IoFuture;

+import org.apache.mina.common.IoFutureListener;

+import org.apache.mina.common.IoSession;

+import org.apache.mina.common.ThreadModel;

+import org.apache.mina.filter.codec.ProtocolCodecFilter;

+import org.apache.mina.transport.socket.nio.SocketConnector;

+import org.apache.mina.transport.socket.nio.SocketConnectorConfig;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.transport.AbstractClient;

+
+/**
+ * Mina client.
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class MinaClient extends AbstractClient {

+    

+    private static final Logger logger = LoggerFactory.getLogger(MinaClient.class);
+
+    private static final Map<String, SocketConnector> connectors = new ConcurrentHashMap<String, SocketConnector>();
+    
+    private String connectorKey;
+    
+    private SocketConnector connector;
+    
+    private volatile IoSession session; // volatile, please copy reference to use
+
+    public MinaClient(final URL url, final ChannelHandler handler) throws RemotingException {
+        super(url, wrapChannelHandler(url, handler));
+    }

+    
+    @Override
+    protected void doOpen() throws Throwable {
+        connectorKey = getUrl().toFullString();
+        SocketConnector c = connectors.get(connectorKey);
+        if (c != null) {
+            connector = c;
+        } else {
+            // set thread pool.
+            connector = new SocketConnector(Constants.DEFAULT_IO_THREADS, 
+                                            Executors.newCachedThreadPool(new NamedThreadFactory("MinaClientWorker", true)));
+            // config
+            SocketConnectorConfig cfg = (SocketConnectorConfig) connector.getDefaultConfig();
+            cfg.setThreadModel(ThreadModel.MANUAL);
+            cfg.getSessionConfig().setTcpNoDelay(true);
+            cfg.getSessionConfig().setKeepAlive(true);
+            int timeout = getTimeout();
+            cfg.setConnectTimeout(timeout < 1000 ? 1 : timeout / 1000);
+            // set codec.
+            connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MinaCodecAdapter(getCodec(), getUrl(), this)));
+            connectors.put(connectorKey, connector);
+        }
+    }
+    
+    @Override
+    protected void doConnect() throws Throwable {
+        ConnectFuture future = connector.connect(getConnectAddress(), new MinaHandler(getUrl(), this));
+        long start = System.currentTimeMillis();
+        final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
+        final CountDownLatch finish = new CountDownLatch(1); // resolve future.awaitUninterruptibly() dead lock
+        future.addListener(new IoFutureListener() {
+            public void operationComplete(IoFuture future) {
+                try {
+                    if (future.isReady()) {
+                        IoSession newSession = future.getSession();
+                        try {
+                            // 关闭旧的连接
+                            IoSession oldSession = MinaClient.this.session; // copy reference
+                            if (oldSession != null) {
+                                try {
+                                    if (logger.isInfoEnabled()) {
+                                        logger.info("Close old mina channel " + oldSession + " on create new mina channel " + newSession);
+                                    }
+                                    oldSession.close();
+                                } finally {
+                                    MinaChannel.removeChannelIfDisconnectd(oldSession);
+                                }
+                            }
+                        } finally {
+                            if (MinaClient.this.isClosed()) {
+                                try {
+                                    if (logger.isInfoEnabled()) {
+                                        logger.info("Close new mina channel " + newSession + ", because the client closed.");
+                                    }
+                                    newSession.close();
+                                } finally {
+                                    MinaClient.this.session = null;
+                                    MinaChannel.removeChannelIfDisconnectd(newSession);
+                                }
+                            } else {
+                                MinaClient.this.session = newSession;
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    exception.set(e);
+                } finally {
+                    finish.countDown();
+                }
+            }
+        });
+        try {
+            finish.await(getTimeout(), TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server " + getRemoteAddress() + " client-side timeout "
+                                        + getTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start)
+                                        + "ms) from netty client " + NetUtils.getLocalHost() + " using dubbo version "
+                                        + Version.getVersion() + ", cause: " + e.getMessage(), e);
+        }
+        Throwable e = exception.get();
+        if (e != null) {
+            throw e;
+        }
+    }
+
+    @Override
+    protected void doDisConnect() throws Throwable {
+        try {
+            MinaChannel.removeChannelIfDisconnectd(session);
+        } catch (Throwable t) {
+            logger.warn(t.getMessage());
+        }
+    }
+
+    @Override
+    protected void doClose() throws Throwable {
+        //release mina resouces.
+    }
+    
+    @Override
+    protected Channel getChannel() {
+        IoSession s = session;
+        if (s == null || ! s.isConnected())
+            return null;
+        return MinaChannel.getOrAddChannel(s, getUrl(), this);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaCodecAdapter.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaCodecAdapter.java
new file mode 100644
index 0000000..48c52f7
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaCodecAdapter.java
@@ -0,0 +1,193 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.mina;
+
+import java.io.IOException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.exchange.Response;
+
+/**
+ * MinaCodecAdapter.
+ * 
+ * @author qian.lei
+ */
+final class MinaCodecAdapter implements ProtocolCodecFactory {
+
+    private static final String   BUFFER_KEY          = MinaCodecAdapter.class.getName() + ".BUFFER";
+
+    private final ProtocolEncoder encoder            = new InternalEncoder();
+
+    private final ProtocolDecoder decoder            = new InternalDecoder();
+
+    private final Codec           upstreamCodec;
+    private final Codec           downstreamCodec;
+
+    private final URL             url;
+    
+    private final ChannelHandler  handler;
+
+    private final int            bufferSize;
+    
+    public MinaCodecAdapter(Codec codec, URL url, ChannelHandler handler){
+        this(codec,codec,url,handler);
+    }
+    
+    /**
+     * server 端如果有消息发送需要分开codec,默认的上行code是dubbo1兼容的
+     */
+    public MinaCodecAdapter(Codec upstreamCodec, Codec downstreamCodec, URL url, ChannelHandler handler){
+        this.upstreamCodec = upstreamCodec;
+        this.downstreamCodec = downstreamCodec;
+        this.url = url;
+        this.handler = handler;
+        int b = url.getPositiveParameter(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);

+        this.bufferSize = b >= Constants.MIN_BUFFER_SIZE && b <= Constants.MAX_BUFFER_SIZE ? b : Constants.DEFAULT_BUFFER_SIZE;
+    }
+
+    public ProtocolEncoder getEncoder() {
+        return encoder;
+    }
+
+    public ProtocolDecoder getDecoder() {
+        return decoder;
+    }
+
+    private class InternalEncoder implements ProtocolEncoder {
+
+        public void dispose(IoSession session) throws Exception {
+        }
+
+        public void encode(IoSession session, Object msg, ProtocolEncoderOutput out) throws Exception {
+            UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(1024); // 不需要关闭
+            MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+            try {
+                if(!(msg instanceof Response)){
+                    downstreamCodec.encode(channel, os, msg);
+                }else{
+                    upstreamCodec.encode(channel, os, msg);
+                }
+                
+            } finally {
+                MinaChannel.removeChannelIfDisconnectd(session);
+            }
+            out.write(ByteBuffer.wrap(os.toByteArray()));
+            out.flush();
+        }
+    }
+
+    private class InternalDecoder implements ProtocolDecoder {
+
+        public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception {
+            int readable = in.limit();
+            if (readable <= 0) return;
+
+            int off, limit;
+            byte[] buf;
+            // load buffer from context.
+            Object[] tmp = (Object[]) session.getAttribute(BUFFER_KEY);
+            if (tmp == null) {
+                buf = new byte[bufferSize];
+                off = limit = 0;
+            } else {
+                buf = (byte[]) tmp[0];
+                off = (Integer) tmp[1];
+                limit = (Integer) tmp[2];
+            }
+
+            Channel channel = MinaChannel.getOrAddChannel(session, url, handler);
+            boolean remaining = true;
+            Object msg;
+            UnsafeByteArrayInputStream bis;
+            try {
+                do {
+                    // read data into buffer.
+                    int read = Math.min(readable, buf.length - limit);
+                    in.get(buf, limit, read);
+                    limit += read;
+                    readable -= read;
+                    bis = new UnsafeByteArrayInputStream(buf, off, limit - off); // 不需要关闭
+                    // decode object.
+                    do {
+                        try {
+                            msg = upstreamCodec.decode(channel, bis);
+                        } catch (IOException e) {
+                            remaining = false;
+                            throw e;
+                        }
+                        if (msg == Codec.NEED_MORE_INPUT) {
+                            if (off == 0) {
+                                if (readable > 0) {
+                                    buf = Bytes.copyOf(buf, buf.length << 1);
+                                }
+                            } else {
+                                int len = limit - off;
+                                System.arraycopy(buf, off, buf, 0, len);
+                                off = 0;
+                                limit = len;
+                            }
+                            break;
+                        } else {
+                            int pos = bis.position();
+                            if (pos == off) {
+                                remaining = false;
+                                throw new IOException("Decode without read data.");
+                            }
+                            if (msg != null) {
+                                out.write(msg);
+                            }
+                            off = pos;
+                        }
+                    } while (bis.available() > 0);
+                } while (readable > 0);
+            } finally {
+                if (remaining) {
+                    int len = limit - off;
+                    if (len < buf.length / 2) {
+                        System.arraycopy(buf, off, buf, 0, len);
+                        off = 0;
+                        limit = len;
+                    }
+                    session.setAttribute(BUFFER_KEY, new Object[] { buf, off, limit });
+                } else {
+                    session.removeAttribute(BUFFER_KEY);
+                }
+                MinaChannel.removeChannelIfDisconnectd(session);
+            }
+        }
+
+        public void dispose(IoSession session) throws Exception {
+        }
+
+        public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaHandler.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaHandler.java
new file mode 100644
index 0000000..75108f5
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaHandler.java
@@ -0,0 +1,96 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.mina;
+
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+
+/**
+ * MinaHandler
+ * 
+ * @author william.liangf
+ */
+public class MinaHandler extends IoHandlerAdapter {
+
+    private final URL url;
+    
+    private final ChannelHandler handler;
+    
+    public MinaHandler(URL url, ChannelHandler handler) {
+        if (url == null) {
+            throw new IllegalArgumentException("url == null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("handler == null");
+        }
+        this.url = url;
+        this.handler = handler;
+    }
+
+    @Override
+    public void sessionOpened(IoSession session) throws Exception {
+        MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+        try {
+            handler.connected(channel);
+        } finally {
+            MinaChannel.removeChannelIfDisconnectd(session);
+        }
+    }
+
+    @Override
+    public void sessionClosed(IoSession session) throws Exception {
+        MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+        try {
+            handler.disconnected(channel);
+        } finally {
+            MinaChannel.removeChannelIfDisconnectd(session);
+        }
+    }
+
+    @Override
+    public void messageReceived(IoSession session, Object message) throws Exception {
+        MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+        try {
+            handler.received(channel, message);
+        } finally {
+            MinaChannel.removeChannelIfDisconnectd(session);
+        }
+    }
+
+    @Override
+    public void messageSent(IoSession session, Object message) throws Exception {
+        MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+        try {
+            handler.sent(channel, message);
+        } finally {
+            MinaChannel.removeChannelIfDisconnectd(session);
+        }
+    }
+
+    @Override
+    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
+        MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+        try {
+            handler.caught(channel, cause);
+        } finally {
+            MinaChannel.removeChannelIfDisconnectd(session);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaServer.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaServer.java
new file mode 100644
index 0000000..556af85
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaServer.java
@@ -0,0 +1,109 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.mina;
+
+import java.net.InetSocketAddress;

+import java.util.Collection;

+import java.util.HashSet;

+import java.util.Set;

+import java.util.concurrent.Executors;

+

+import org.apache.mina.common.IoSession;

+import org.apache.mina.common.ThreadModel;

+import org.apache.mina.filter.codec.ProtocolCodecFilter;

+import org.apache.mina.transport.socket.nio.SocketAcceptor;

+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.transport.AbstractServer;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelHandlers;

+
+/**
+ * MinaServer
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ * @author ding.lid
+ */
+public class MinaServer extends AbstractServer {

+    

+    private static final Logger logger = LoggerFactory.getLogger(MinaServer.class);
+
+    private SocketAcceptor acceptor;
+
+    public MinaServer(URL url, ChannelHandler handler) throws RemotingException{
+        super(url, ChannelHandlers.wrap(handler, url.addParameterIfAbsent(Constants.THREAD_NAME_KEY, SERVER_THREAD_POOL_NAME)));
+    }
+
+    @Override
+    protected void doOpen() throws Throwable {
+        // set thread pool.
+        acceptor = new SocketAcceptor(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
+                                       Executors.newCachedThreadPool(new NamedThreadFactory("MinaServerWorker",
+                                                                                            true)));
+        // config
+        SocketAcceptorConfig cfg = (SocketAcceptorConfig) acceptor.getDefaultConfig();
+        cfg.setThreadModel(ThreadModel.MANUAL);
+        // set codec.
+        acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MinaCodecAdapter(getCodec(), getDownstreamCodec(), getUrl(), this)));
+        
+        acceptor.bind(getBindAddress(), new MinaHandler(getUrl(), this));
+    }
+
+    @Override
+    protected void doClose() throws Throwable {
+        try {
+            if (acceptor != null) {
+                acceptor.unbind(getBindAddress());
+            }
+        } catch (Throwable e) {
+            logger.warn(e.getMessage(), e);
+        }
+    }
+
+    public Collection<Channel> getChannels() {
+        Set<IoSession> sessions = acceptor.getManagedSessions(getBindAddress());
+        Collection<Channel> channels = new HashSet<Channel>();
+        for (IoSession session : sessions) {
+            if (session.isConnected()) {
+                channels.add(MinaChannel.getOrAddChannel(session, getUrl(), this));
+            }
+        }
+        return channels;
+    }
+
+    public Channel getChannel(InetSocketAddress remoteAddress) {
+        Set<IoSession> sessions = acceptor.getManagedSessions(getBindAddress());
+        for (IoSession session : sessions) {
+            if (session.getRemoteAddress().equals(remoteAddress)) {
+                return MinaChannel.getOrAddChannel(session, getUrl(), this);
+            }
+        }
+        return null;
+    }
+
+    public boolean isBound() {
+        return acceptor.isManaged(getBindAddress());
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.java
new file mode 100644
index 0000000..534f7f3
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.mina;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.Transporter;

+
+/**
+ * @author ding.lid
+ */
+public class MinaTransporter implements Transporter {
+    
+    public static final String NAME = "mina";
+
+    public Server bind(URL url, ChannelHandler handler) throws RemotingException {
+        return new MinaServer(url, handler);
+    }
+
+    public Client connect(URL url, ChannelHandler handler) throws RemotingException {
+        return new MinaClient(url, handler);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting-mina/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..237b77c
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+mina=com.alibaba.dubbo.remoting.transport.mina.MinaTransporter
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientToServerTest.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientToServerTest.java
new file mode 100644
index 0000000..d2b496f
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientToServerTest.java
@@ -0,0 +1,93 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.remoting.transport.mina;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * ClientToServer
+ * 
+ * @author william.liangf
+ */
+public abstract class ClientToServerTest extends TestCase {
+    
+    protected static final String LOCALHOST = "127.0.0.1";
+    
+    protected ExchangeServer server;
+    
+    protected ExchangeChannel client;
+    
+    protected WorldHandler handler = new WorldHandler();
+    
+    protected abstract ExchangeServer newServer(int port, Replier<?> receiver) throws RemotingException;
+    
+    protected abstract ExchangeChannel newClient(int port) throws RemotingException;
+    
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        int port = (int) (1000 * Math.random() + 10000);
+        server = newServer(port, handler);
+        client = newClient(port);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        try {
+            if (server != null)
+                server.close();
+        } finally {
+            if (client != null)
+                client.close();
+        }
+    }
+
+    @Test
+    public void testFuture() throws Exception {
+        ResponseFuture future = client.request(new World("world"));
+        Hello result = (Hello)future.get();
+        Assert.assertEquals("hello,world", result.getName());
+    }
+
+//    @Test
+//    public void testCallback() throws Exception {
+//        final Object waitter = new Object();
+//        client.invoke(new World("world"), new InvokeCallback<Hello>() {
+//            public void callback(Hello result) {
+//                Assert.assertEquals("hello,world", result.getName());
+//                synchronized (waitter) {
+//                    waitter.notifyAll();
+//                }
+//            }
+//            public void onException(Throwable exception) {
+//            }
+//        });
+//        synchronized (waitter) {
+//            waitter.wait();
+//        }
+//    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientsTest.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientsTest.java
new file mode 100644
index 0000000..98de2ea
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientsTest.java
@@ -0,0 +1,62 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.remoting.transport.mina;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertNull;

+import static org.junit.Assert.assertThat;

+import static org.junit.Assert.fail;

+import static org.junit.matchers.JUnitMatchers.containsString;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.Transporter;

+import com.alibaba.dubbo.remoting.transport.mina.MinaTransporter;

+

+/**

+ * @author ding.lid

+ */

+public class ClientsTest {

+

+    @Test

+    public void testGetTransportEmpty() {

+        try {

+            ExtensionLoader.getExtensionLoader(Transporter.class).getExtension("");

+            fail();

+        } catch (IllegalArgumentException expected) {

+            assertThat(expected.getMessage(), containsString("Extension name == null"));

+        }

+    }

+

+    @Test(expected = IllegalArgumentException.class)

+    public void testGetTransportNull() {

+        String name = null;

+        ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name);

+    }

+

+    @Test

+    public void testGetTransport1() {

+        String name = "mina";

+        assertEquals(MinaTransporter.class, ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());

+    }

+

+    @Test(expected = IllegalStateException.class)

+    public void testGetTransportWrong() {

+        String name = "nety";

+        assertNull(ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/Hello.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/Hello.java
new file mode 100644
index 0000000..eca6042
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/Hello.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.remoting.transport.mina;
+
+import java.io.Serializable;
+
+/**
+ * Result
+ * 
+ * @author william.liangf
+ */
+public class Hello implements Serializable {
+
+    private static final long serialVersionUID = 8563900571013747774L;
+    
+    private String name;
+    
+    public Hello() {
+    }
+
+    public Hello(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/MinaClientToServerTest.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/MinaClientToServerTest.java
new file mode 100644
index 0000000..b0313ce
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/MinaClientToServerTest.java
@@ -0,0 +1,42 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.remoting.transport.mina;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * MinaServerClientTest
+ * 
+ * @author william.liangf
+ */
+public class MinaClientToServerTest extends ClientToServerTest {
+
+    @Override
+    protected ExchangeServer newServer(int port, Replier<?> receiver) throws RemotingException {
+        return Exchangers.bind(URL.valueOf("exchange://localhost:" + port + "?server=mina"), receiver);
+    }
+
+    @Override
+    protected ExchangeChannel newClient(int port) throws RemotingException {
+        return Exchangers.connect(URL.valueOf("exchange://localhost:" + port + "?client=mina"));
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/World.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/World.java
new file mode 100644
index 0000000..bfec203
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/World.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.remoting.transport.mina;
+
+import java.io.Serializable;
+
+/**
+ * Data
+ * 
+ * @author william.liangf
+ */
+public class World implements Serializable {
+
+    private static final long serialVersionUID = 8563900571013747774L;
+    
+    private String name;
+    
+    public World() {
+    }
+
+    public World(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/WorldHandler.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/WorldHandler.java
new file mode 100644
index 0000000..1175ab3
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/WorldHandler.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.remoting.transport.mina;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * DataHandler
+ * 
+ * @author william.liangf
+ */
+public class WorldHandler implements Replier<World> {
+
+    public Class<World> interest() {
+        return World.class;
+    }
+
+    public Object reply(ExchangeChannel channel, World msg) throws RemotingException {
+        return new Hello("hello," + msg.getName());
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/pom.xml b/dubbo-remoting-netty/pom.xml
new file mode 100644
index 0000000..c3c481b
--- /dev/null
+++ b/dubbo-remoting-netty/pom.xml
@@ -0,0 +1,42 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-remoting-netty</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Netty Remoting Module</name>
+	<description>The netty remoting module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.netty</groupId>
+			<artifactId>netty</artifactId>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyChannel.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyChannel.java
new file mode 100644
index 0000000..7335c17
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyChannel.java
@@ -0,0 +1,188 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import java.net.InetSocketAddress;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.jboss.netty.channel.ChannelFuture;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.AbstractChannel;
+
+/**
+ * NettyChannel.
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ */
+final class NettyChannel extends AbstractChannel {
+
+    private static final Logger logger = LoggerFactory.getLogger(NettyChannel.class);
+
+    private static final ConcurrentMap<org.jboss.netty.channel.Channel, NettyChannel> channelMap = new ConcurrentHashMap<org.jboss.netty.channel.Channel, NettyChannel>();
+
+    private final org.jboss.netty.channel.Channel channel;
+
+    private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
+
+    private NettyChannel(org.jboss.netty.channel.Channel channel, URL url, ChannelHandler handler){
+        super(url, handler);
+        if (channel == null) {
+            throw new IllegalArgumentException("netty channel == null;");
+        }
+        this.channel = channel;
+    }
+
+    static NettyChannel getOrAddChannel(org.jboss.netty.channel.Channel ch, URL url, ChannelHandler handler) {
+        if (ch == null) {
+            return null;
+        }
+        NettyChannel ret = channelMap.get(ch);
+        if (ret == null) {
+            NettyChannel nc = new NettyChannel(ch, url, handler);
+            if (ch.isConnected()) {
+                ret = channelMap.putIfAbsent(ch, nc);
+            }
+            if (ret == null) {
+                ret = nc;
+            }
+        }
+        return ret;
+    }
+
+    static void removeChannelIfDisconnected(org.jboss.netty.channel.Channel ch) {
+        if (ch != null && ! ch.isConnected()) {
+            channelMap.remove(ch);
+        }
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        return (InetSocketAddress) channel.getLocalAddress();
+    }
+
+    public InetSocketAddress getRemoteAddress() {
+        return (InetSocketAddress) channel.getRemoteAddress();
+    }
+
+    public boolean isConnected() {
+        return channel.isConnected();
+    }
+
+    public void send(Object message, boolean sent) throws RemotingException {
+        super.send(message, sent);
+        
+        boolean success = true;
+        int timeout = 0;
+        try {
+            ChannelFuture future = channel.write(message);
+            if (sent) {
+                timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+                success = future.await(timeout);

+            }

+            Throwable cause = future.getCause();

+            if (cause != null) {

+                throw cause;

+            }
+        } catch (Throwable e) {
+            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);
+        }
+        
+        if(! success) {

+            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
+                    + "in timeout(" + timeout + "ms) limit");
+        }
+    }
+
+    public void close() {
+        try {
+            super.close();
+        } catch (Exception e) {
+            logger.warn(e.getMessage(), e);
+        }
+        try {
+            removeChannelIfDisconnected(channel);
+        } catch (Exception e) {
+            logger.warn(e.getMessage(), e);
+        }
+        try {
+            attributes.clear();
+        } catch (Exception e) {
+            logger.warn(e.getMessage(), e);
+        }
+        try {
+            if (logger.isInfoEnabled()) {
+                logger.info("Close netty channel " + channel);
+            }
+            channel.close();
+        } catch (Exception e) {
+            logger.warn(e.getMessage(), e);
+        }
+    }
+
+    public boolean hasAttribute(String key) {
+        return attributes.containsKey(key);
+    }
+    
+    public Object getAttribute(String key) {
+        return attributes.get(key);
+    }
+
+    public void setAttribute(String key, Object value) {
+        if (value == null) { // The null value unallowed in the ConcurrentHashMap.
+            attributes.remove(key);
+        } else {
+            attributes.put(key, value);
+        }
+    }
+
+    public void removeAttribute(String key) {
+        attributes.remove(key);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((channel == null) ? 0 : channel.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        NettyChannel other = (NettyChannel) obj;
+        if (channel == null) {
+            if (other.channel != null) return false;
+        } else if (!channel.equals(other.channel)) return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "NettyChannel [channel=" + channel + "]";
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyClient.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyClient.java
new file mode 100644
index 0000000..59268af
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyClient.java
@@ -0,0 +1,164 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import java.util.concurrent.Executors;

+import java.util.concurrent.TimeUnit;

+

+import org.jboss.netty.bootstrap.ClientBootstrap;

+import org.jboss.netty.channel.Channel;

+import org.jboss.netty.channel.ChannelFactory;

+import org.jboss.netty.channel.ChannelFuture;

+import org.jboss.netty.channel.ChannelPipeline;

+import org.jboss.netty.channel.ChannelPipelineFactory;

+import org.jboss.netty.channel.Channels;

+import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.transport.AbstractClient;

+
+/**
+ * NettyClient.
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class NettyClient extends AbstractClient {

+    

+    private static final Logger logger = LoggerFactory.getLogger(NettyClient.class);
+
+    // 因ChannelFactory的关闭有DirectMemory泄露,采用静态化规避
+    // https://issues.jboss.org/browse/NETTY-424
+    private static final ChannelFactory channelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(new NamedThreadFactory("NettyClientBoss", true)), 
+                                                                                           Executors.newCachedThreadPool(new NamedThreadFactory("NettyClientWorker", true)), 
+                                                                                           Constants.DEFAULT_IO_THREADS);
+    private ClientBootstrap bootstrap;
+
+    private volatile Channel channel; // volatile, please copy reference to use
+    
+    public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException{
+        super(url, wrapChannelHandler(url, handler));
+    }
+    
+    @Override
+    protected void doOpen() throws Throwable {
+        bootstrap = new ClientBootstrap(channelFactory);
+        // config
+        // @see org.jboss.netty.channel.socket.SocketChannelConfig
+        bootstrap.setOption("keepAlive", true);
+        bootstrap.setOption("tcpNoDelay", true);
+        bootstrap.setOption("connectTimeoutMillis", getTimeout());
+        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
+        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
+            public ChannelPipeline getPipeline() {
+                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
+                ChannelPipeline pipeline = Channels.pipeline();
+                pipeline.addLast("decoder", adapter.getDecoder());
+                pipeline.addLast("encoder", adapter.getEncoder());
+                pipeline.addLast("handler", nettyHandler);
+                return pipeline;
+            }
+        });
+    }
+
+    protected void doConnect() throws Throwable {

+        long start = System.currentTimeMillis();
+        ChannelFuture future = bootstrap.connect(getConnectAddress());

+        try{

+            boolean ret = future.awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS);

+            

+            if (ret && future.isSuccess()) {

+                Channel newChannel = future.getChannel();

+                newChannel.setInterestOps(Channel.OP_READ_WRITE);

+                try {

+                    // 关闭旧的连接

+                    Channel oldChannel = NettyClient.this.channel; // copy reference

+                    if (oldChannel != null) {

+                        try {

+                            if (logger.isInfoEnabled()) {

+                                logger.info("Close old netty channel " + oldChannel + " on create new netty channel " + newChannel);

+                            }

+                            oldChannel.close();

+                        } finally {

+                            NettyChannel.removeChannelIfDisconnected(oldChannel);

+                        }

+                    }

+                } finally {

+                    if (NettyClient.this.isClosed()) {

+                        try {

+                            if (logger.isInfoEnabled()) {

+                                logger.info("Close new netty channel " + newChannel + ", because the client closed.");

+                            }

+                            newChannel.close();

+                        } finally {

+                            NettyClient.this.channel = null;

+                            NettyChannel.removeChannelIfDisconnected(newChannel);

+                        }

+                    } else {

+                        NettyClient.this.channel = newChannel;

+                    }

+                }

+            } else if (future.getCause() != null) {

+                throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "

+                        + getRemoteAddress() + ", error message is:" + future.getCause().getMessage(), future.getCause());

+            } else {

+                throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "

+                        + getRemoteAddress() + " client-side timeout "

+                        + getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start) + "ms) from netty client "

+                        + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion());

+            }

+        }finally{

+            if (! isConnected()) {

+                future.cancel();

+            }

+        }

+    }
+
+    @Override
+    protected void doDisConnect() throws Throwable {
+        try {
+            NettyChannel.removeChannelIfDisconnected(channel);
+        } catch (Throwable t) {
+            logger.warn(t.getMessage());
+        }
+    }
+    
+    @Override
+    protected void doClose() throws Throwable {
+        /*try {
+            bootstrap.releaseExternalResources();
+        } catch (Throwable t) {
+            logger.warn(t.getMessage());
+        }*/
+    }
+
+    @Override
+    protected com.alibaba.dubbo.remoting.Channel getChannel() {
+        Channel c = channel;
+        if (c == null || ! c.isConnected())
+            return null;
+        return NettyChannel.getOrAddChannel(c, getUrl(), this);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyCodecAdapter.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyCodecAdapter.java
new file mode 100644
index 0000000..c10b9dd
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyCodecAdapter.java
@@ -0,0 +1,203 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import java.io.IOException;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandler;
+import org.jboss.netty.channel.ChannelHandler.Sharable;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
+import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.exchange.Response;
+
+/**
+ * NettyCodecAdapter.
+ * 
+ * @author qian.lei
+ */
+final class NettyCodecAdapter {
+
+    private final ChannelHandler encoder = new InternalEncoder();
+    
+    private final ChannelHandler decoder = new InternalDecoder();
+
+    private final Codec          upstreamCodec;
+    private final Codec          downstreamCodec;
+    
+    private final URL            url;
+    
+    private final int            bufferSize;
+    
+    private final com.alibaba.dubbo.remoting.ChannelHandler handler;
+
+    public NettyCodecAdapter(Codec codec, URL url, com.alibaba.dubbo.remoting.ChannelHandler handler){
+        this(codec, codec, url, handler);
+    }
+    /**
+     * server 端如果有消息发送需要分开codec,默认的上行code是dubbo1兼容的
+     */
+    public NettyCodecAdapter(Codec upstreamCodec, Codec downstreamCodec, URL url, com.alibaba.dubbo.remoting.ChannelHandler handler){
+        this.downstreamCodec = downstreamCodec;
+        this.upstreamCodec = upstreamCodec;
+        this.url = url;
+        this.handler = handler;
+        int b = url.getPositiveParameter(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);

+        this.bufferSize = b >= Constants.MIN_BUFFER_SIZE && b <= Constants.MAX_BUFFER_SIZE ? b : Constants.DEFAULT_BUFFER_SIZE;
+    }
+
+    public ChannelHandler getEncoder() {
+        return encoder;
+    }
+
+    public ChannelHandler getDecoder() {
+        return decoder;
+    }
+
+    @Sharable
+    private class InternalEncoder extends OneToOneEncoder {
+
+        @Override
+        protected Object encode(ChannelHandlerContext ctx, Channel ch, Object msg) throws Exception {
+            UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(1024); // 不需要关闭
+            NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
+            try {
+                if(!(msg instanceof Response)){
+                    downstreamCodec.encode(channel, os, msg);
+                }else {
+                    upstreamCodec.encode(channel, os, msg);
+                }
+                
+            } finally {
+                NettyChannel.removeChannelIfDisconnected(ch);
+            }
+            return ChannelBuffers.wrappedBuffer(os.toByteBuffer());
+        }
+    }
+
+    private class InternalDecoder extends SimpleChannelUpstreamHandler {
+
+        private int    mOffset = 0, mLimit = 0;
+
+        private byte[] mBuffer = null;
+
+        @Override
+        public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
+            Object o = event.getMessage();
+            if (! (o instanceof ChannelBuffer)) {
+                ctx.sendUpstream(event);
+                return;
+            }
+
+            ChannelBuffer input = (ChannelBuffer) o;
+            int readable = input.readableBytes();
+            if (readable <= 0) {
+                return;
+            }
+
+            int off, limit;
+            byte[] buf = mBuffer;
+            if (buf == null) {
+                buf = new byte[bufferSize];
+                off = limit = 0;
+            } else {
+                off = mOffset;
+                limit = mLimit;
+            }
+
+            NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+            boolean remaining = true;
+            Object msg;
+            UnsafeByteArrayInputStream bis;
+            try {
+                do {
+                    // read data into buffer.
+                    int read = Math.min(readable, buf.length - limit);
+                    input.readBytes(buf, limit, read);
+                    limit += read;
+                    readable -= read;
+                    bis = new UnsafeByteArrayInputStream(buf, off, limit - off); // 不需要关闭
+                    // decode object.
+                    do {
+                        try {
+                            msg = upstreamCodec.decode(channel, bis);
+                        } catch (IOException e) {
+                            remaining = false;
+                            throw e;
+                        }
+                        if (msg == Codec.NEED_MORE_INPUT) {
+                            if (off == 0) {
+                                if (readable > 0) {
+                                    buf = Bytes.copyOf(buf, buf.length << 1);
+                                }
+                            } else {
+                                int len = limit - off;
+                                System.arraycopy(buf, off, buf, 0, len); // adjust buffer.
+                                off = 0;
+                                limit = len;
+                            }
+                            break;
+                        } else {
+                            int pos = bis.position();
+                            if (off == pos) {
+                                remaining = false;
+                                throw new IOException("Decode without read data.");
+                            }
+                            if (msg != null) {
+                                Channels.fireMessageReceived(ctx, msg, event.getRemoteAddress());
+                            }
+                            off = pos;
+                        }
+                    } while (bis.available() > 0);
+                } while (readable > 0);
+            } finally {
+                if (remaining) {
+                    int len = limit - off;
+                    if (len < buf.length / 2) {
+                        System.arraycopy(buf, off, buf, 0, len);
+                        off = 0;
+                        limit = len;
+                    }
+                    mBuffer = buf;
+                    mOffset = off;
+                    mLimit = limit;
+                } else {
+                    mBuffer = null;
+                    mOffset = mLimit = 0;
+                }
+                NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+            }
+        }
+
+        @Override
+        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
+            ctx.sendUpstream(e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyHandler.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyHandler.java
new file mode 100644
index 0000000..1a05bd1
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyHandler.java
@@ -0,0 +1,118 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import java.net.InetSocketAddress;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jboss.netty.channel.ChannelHandler.Sharable;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+
+/**
+ * NettyHandler
+ * 
+ * @author william.liangf
+ */
+@Sharable
+public class NettyHandler extends SimpleChannelHandler {
+
+    private final Map<String, Channel> channels = new ConcurrentHashMap<String, Channel>(); // <ip:port, channel>
+    
+    private final URL url;
+    
+    private final ChannelHandler handler;
+    
+    public NettyHandler(URL url, ChannelHandler handler){
+        if (url == null) {
+            throw new IllegalArgumentException("url == null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("handler == null");
+        }
+        this.url = url;
+        this.handler = handler;
+    }
+
+    public Map<String, Channel> getChannels() {
+        return channels;
+    }
+
+    @Override
+    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+        try {
+            if (channel != null) {
+                channels.put(NetUtils.toAddressString((InetSocketAddress) ctx.getChannel().getRemoteAddress()), channel);
+            }
+            handler.connected(channel);
+        } finally {
+            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+        }
+    }
+
+    @Override
+    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {

+        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+        try {
+            channels.remove(NetUtils.toAddressString((InetSocketAddress) ctx.getChannel().getRemoteAddress()));
+            handler.disconnected(channel);
+        } finally {
+            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+        }
+    }
+    
+    @Override
+    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+        try {
+            handler.received(channel, e.getMessage());
+        } finally {
+            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+        }
+    }
+
+    @Override
+    public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+        super.writeRequested(ctx, e);
+        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+        try {
+            handler.sent(channel, e.getMessage());
+        } finally {
+            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+        }
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
+        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+        try {
+            handler.caught(channel, e.getCause());
+        } finally {
+            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyServer.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyServer.java
new file mode 100644
index 0000000..12359f1
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyServer.java
@@ -0,0 +1,156 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;

+

+import java.net.InetSocketAddress;

+import java.util.Collection;

+import java.util.HashSet;

+import java.util.Map;

+import java.util.concurrent.ExecutorService;

+import java.util.concurrent.Executors;

+

+import org.jboss.netty.bootstrap.ServerBootstrap;

+import org.jboss.netty.channel.ChannelFactory;

+import org.jboss.netty.channel.ChannelPipeline;

+import org.jboss.netty.channel.ChannelPipelineFactory;

+import org.jboss.netty.channel.Channels;

+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.transport.AbstractServer;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelHandlers;

+

+/**

+ * NettyServer

+ * 

+ * @author qian.lei

+ * @author chao.liuc

+ */

+public class NettyServer extends AbstractServer implements Server {

+    

+    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);

+

+    private Map<String, Channel>  channels; // <ip:port, channel>

+

+    private ServerBootstrap                 bootstrap;

+

+    private org.jboss.netty.channel.Channel channel;

+

+    public NettyServer(URL url, ChannelHandler handler) throws RemotingException{

+        super(url, ChannelHandlers.wrap(handler, url.addParameterIfAbsent(Constants.THREAD_NAME_KEY, SERVER_THREAD_POOL_NAME)));

+    }

+

+    @Override

+    protected void doOpen() throws Throwable {

+        ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));

+        ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));

+        ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));

+        bootstrap = new ServerBootstrap(channelFactory);

+        

+        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);

+        channels = nettyHandler.getChannels();

+        // https://issues.jboss.org/browse/NETTY-365

+        // https://issues.jboss.org/browse/NETTY-379

+        // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));

+        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {

+            public ChannelPipeline getPipeline() {

+                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getDownstreamCodec(), getUrl(), NettyServer.this);

+                ChannelPipeline pipeline = Channels.pipeline();

+                /*int idleTimeout = getIdleTimeout();

+                if (idleTimeout > 10000) {

+                    pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));

+                }*/

+                pipeline.addLast("decoder", adapter.getDecoder());

+                pipeline.addLast("encoder", adapter.getEncoder());

+                pipeline.addLast("handler", nettyHandler);

+                return pipeline;

+            }

+        });

+        // bind

+        channel = bootstrap.bind(getBindAddress());

+    }

+

+    @Override

+    protected void doClose() throws Throwable {

+        try {

+            if (channel != null) {

+                // unbind.

+                channel.close();

+            }

+        } catch (Throwable e) {

+            logger.warn(e.getMessage(), e);

+        }

+        try {

+            Collection<com.alibaba.dubbo.remoting.Channel> channels = getChannels();

+            if (channels != null && channels.size() > 0) {

+                for (com.alibaba.dubbo.remoting.Channel channel : channels) {

+                    try {

+                        channel.close();

+                    } catch (Throwable e) {

+                        logger.warn(e.getMessage(), e);

+                    }

+                }

+            }

+        } catch (Throwable e) {

+            logger.warn(e.getMessage(), e);

+        }

+        try {

+            if (bootstrap != null) { 

+                // release external resource.

+                bootstrap.releaseExternalResources();

+            }

+        } catch (Throwable e) {

+            logger.warn(e.getMessage(), e);

+        }

+        try {

+            if (channels != null) {

+                channels.clear();

+            }

+        } catch (Throwable e) {

+            logger.warn(e.getMessage(), e);

+        }

+    }

+    

+    public Collection<Channel> getChannels() {

+        Collection<Channel> chs = new HashSet<Channel>();

+        for (Channel channel : this.channels.values()) {

+            if (channel.isConnected()) {

+                chs.add(channel);

+            } else {

+                channels.remove(NetUtils.toAddressString(channel.getRemoteAddress()));

+            }

+        }

+        return chs;

+    }

+

+    public Channel getChannel(InetSocketAddress remoteAddress) {

+        return channels.get(NetUtils.toAddressString(remoteAddress));

+    }

+

+    public boolean isBound() {

+        return channel.isBound();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.java
new file mode 100644
index 0000000..f899a6f
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.Transporter;

+
+/**
+ * @author ding.lid
+ */
+public class NettyTransporter implements Transporter {
+
+    public static final String NAME = "netty";
+    
+    public Server bind(URL url, ChannelHandler listener) throws RemotingException {
+        return new NettyServer(url, listener);
+    }
+
+    public Client connect(URL url, ChannelHandler listener) throws RemotingException {
+        return new NettyClient(url, listener);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting-netty/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..eb9bcb2
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+netty=com.alibaba.dubbo.remoting.transport.netty.NettyTransporter
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientReconnectTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientReconnectTest.java
new file mode 100644
index 0000000..8b3887b
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientReconnectTest.java
@@ -0,0 +1,154 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import org.apache.log4j.Level;

+import org.junit.Assert;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.utils.DubboAppender;

+import com.alibaba.dubbo.common.utils.LogUtil;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.exchange.Exchangers;

+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;

+
+/**
+ * 客户端重连测试
+ * @author chao.liuc
+ *
+ */
+public class ClientReconnectTest {
+    @Test
+    public void testReconnect() throws RemotingException, InterruptedException{
+        {
+            int port = NetUtils.getAvailablePort();
+            Client client = startClient(port, 200);
+            Assert.assertEquals(false, client.isConnected());
+            Server server = startServer(port);
+            for (int i = 0; i < 100 && ! client.isConnected(); i++) {
+                Thread.sleep(10);
+            }
+            Assert.assertEquals(true, client.isConnected());
+            client.close(2000);
+            server.close(2000);
+        }
+        {
+            int port = NetUtils.getAvailablePort();
+            Client client = startClient(port, 20000);
+            Assert.assertEquals(false, client.isConnected());
+            Server server = startServer(port);
+            for(int i=0;i<5;i++){
+                Thread.sleep(200);
+            }
+            Assert.assertEquals(false, client.isConnected());
+            client.close(2000);
+            server.close(2000);
+        }
+    }

+    

+    /**

+     * 重连日志的校验

+     */

+    @Test

+    public void testReconnectNoLog() throws RemotingException, InterruptedException{

+        int port = NetUtils.getAvailablePort();

+        DubboAppender.doStart();

+        String url = "exchange://127.0.0.2:"+port + "/client.reconnect.test?check=false&"

+        +Constants.RECONNECT_KEY+"="+1 ; //1ms reconnect,保证有足够频率的重连

+        try{

+            Exchangers.connect(url);

+        }catch (Exception e) {

+            //do nothing

+        }

+        Thread.sleep(1000);//重连线程的运行

+        Assert.assertEquals("no error message ", 0 , LogUtil.findMessage(Level.ERROR, "client reconnect to "));

+        Assert.assertEquals("no warn message ", 0 , LogUtil.findMessage(Level.WARN, "client reconnect to "));

+        DubboAppender.doStop();

+    }

+  

+    /**

+     * 重连日志的校验,不能一直抛出error日志.

+     */

+    @Test

+    public void testReconnectErrorLog() throws RemotingException, InterruptedException{

+        int port = NetUtils.getAvailablePort();

+        DubboAppender.doStart();

+        String url = "exchange://127.0.0.3:"+port + "/client.reconnect.test?check=false&"

+        +Constants.RECONNECT_KEY+"="+1 + //1ms reconnect,保证有足够频率的重连

+        "&"+Constants.SHUTDOWN_TIMEOUT_KEY+ "=0";//shutdown时间足够短,确保error日志输出

+        try{

+            Exchangers.connect(url);

+        }catch (Exception e) {

+            //do nothing

+        }

+        Thread.sleep(1000);//重连线程的运行

+        Assert.assertEquals("only one error message ", 1 , LogUtil.findMessage(Level.ERROR, "client reconnect to "));

+        Assert.assertEquals("no warn message ", 0 , LogUtil.findMessage(Level.WARN, "client reconnect to "));

+        DubboAppender.doStop();

+    }

+    

+    /**

+     * 重连日志的校验

+     */

+    @Test

+    public void testReconnectWaringLog() throws RemotingException, InterruptedException{

+        int port = NetUtils.getAvailablePort();

+        DubboAppender.doStart();

+        String url = "exchange://127.0.0.4:"+port + "/client.reconnect.test?check=false&"

+        +Constants.RECONNECT_KEY+"="+1 //1ms reconnect,保证有足够频率的重连

+        +"&"+Constants.SHUTDOWN_TIMEOUT_KEY+ "=1"//shutdown时间足够短,确保error日志输出

+        +"&reconnect.waring.period=100";//每隔多少warning记录一次

+        try{

+            Exchangers.connect(url);

+        }catch (Exception e) {

+            //do nothing

+        }

+        int count =  0;

+        for (int i=0;i<100;i++){

+            count =  LogUtil.findMessage(Level.WARN, "client reconnect to ") ; 

+            if (count >=1){

+                break;

+            }

+            Thread.sleep(50);//重连线程的运行

+        }

+        Assert.assertTrue("warning message count must >= 1, real :"+count, count>= 1);

+        DubboAppender.doStop();

+    }
+    
+    public Client startClient(int port , int reconnectPeriod) throws RemotingException{
+        final String url = "exchange://127.0.0.1:"+port + "/client.reconnect.test?check=false&"+Constants.RECONNECT_KEY+"="+reconnectPeriod;
+        return Exchangers.connect(url);
+    }
+    
+    public Server startServer(int port) throws RemotingException{
+        final String url = "exchange://127.0.0.1:"+port +"/client.reconnect.test";
+        return Exchangers.bind(url, new HandlerAdapter());
+    }
+    
+    static class HandlerAdapter extends ExchangeHandlerAdapter{
+        public void connected(Channel channel) throws RemotingException {
+        }
+        public void disconnected(Channel channel) throws RemotingException {
+        }
+        public void caught(Channel channel, Throwable exception) throws RemotingException {
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientToServerTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientToServerTest.java
new file mode 100644
index 0000000..09e6652
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientToServerTest.java
@@ -0,0 +1,93 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * ClientToServer
+ * 
+ * @author william.liangf
+ */
+public abstract class ClientToServerTest extends TestCase {
+    
+    protected static final String LOCALHOST = "127.0.0.1";
+    
+    protected ExchangeServer server;
+    
+    protected ExchangeChannel client;
+    
+    protected WorldHandler handler = new WorldHandler();
+    
+    protected abstract ExchangeServer newServer(int port, Replier<?> receiver) throws RemotingException;
+    
+    protected abstract ExchangeChannel newClient(int port) throws RemotingException;
+    
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        int port = (int) (1000 * Math.random() + 10000);
+        server = newServer(port, handler);
+        client = newClient(port);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        try {
+            if (server != null)
+                server.close();
+        } finally {
+            if (client != null)
+                client.close();
+        }
+    }
+
+    @Test
+    public void testFuture() throws Exception {
+        ResponseFuture future = client.request(new World("world"));
+        Hello result = (Hello)future.get();
+        Assert.assertEquals("hello,world", result.getName());
+    }
+
+//    @Test
+//    public void testCallback() throws Exception {
+//        final Object waitter = new Object();
+//        client.invoke(new World("world"), new InvokeCallback<Hello>() {
+//            public void callback(Hello result) {
+//                Assert.assertEquals("hello,world", result.getName());
+//                synchronized (waitter) {
+//                    waitter.notifyAll();
+//                }
+//            }
+//            public void onException(Throwable exception) {
+//            }
+//        });
+//        synchronized (waitter) {
+//            waitter.wait();
+//        }
+//    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientsTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientsTest.java
new file mode 100644
index 0000000..10aa3d7
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientsTest.java
@@ -0,0 +1,61 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertNull;

+import static org.junit.Assert.assertThat;

+import static org.junit.Assert.fail;

+import static org.junit.matchers.JUnitMatchers.containsString;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.Transporter;

+

+/**

+ * @author ding.lid

+ */

+public class ClientsTest {

+

+    @Test

+    public void testGetTransportEmpty() {

+        try {

+            ExtensionLoader.getExtensionLoader(Transporter.class).getExtension("");

+            fail();

+        } catch (IllegalArgumentException expected) {

+            assertThat(expected.getMessage(), containsString("Extension name == null"));

+        }

+    }

+

+    @Test(expected = IllegalArgumentException.class)

+    public void testGetTransportNull() {

+        String name = null;

+        ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name);

+    }

+

+    @Test

+    public void testGetTransport3() {

+        String name = "netty";

+        assertEquals(NettyTransporter.class, ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());

+    }

+

+    @Test(expected = IllegalStateException.class)

+    public void testGetTransportWrong() {

+        String name = "nety";

+        assertNull(ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/Hello.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/Hello.java
new file mode 100644
index 0000000..0af10cb
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/Hello.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import java.io.Serializable;
+
+/**
+ * Result
+ * 
+ * @author william.liangf
+ */
+public class Hello implements Serializable {
+
+    private static final long serialVersionUID = 8563900571013747774L;
+    
+    private String name;
+    
+    public Hello() {
+    }
+
+    public Hello(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.java
new file mode 100644
index 0000000..c20d78a
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.java
@@ -0,0 +1,73 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+
+/**
+ * User: heyman
+ * Date: 5/3/11
+ * Time: 5:47 PM
+ */
+public class NettyClientTest {
+    static Server server;
+
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        server = Exchangers.bind(URL.valueOf("exchange://localhost:10001?server=netty"), new TelnetServerHandler());
+    }
+
+    @Test
+    public void testClientClose() throws Exception {
+        List<ExchangeChannel> clients = new ArrayList<ExchangeChannel>(100);
+        for (int i = 0; i < 100; i++) {
+            ExchangeChannel client = Exchangers.connect(URL.valueOf("exchange://localhost:10001?client=netty"));
+            Thread.sleep(5);
+            clients.add(client);
+        }
+        for (ExchangeChannel client : clients){
+            client.close();
+        }
+        Thread.sleep(1000);
+    }
+
+    @Test
+    public void testServerClose() throws Exception {
+        for (int i = 0; i < 100; i++) {
+            Server aServer = Exchangers.bind(URL.valueOf("exchange://localhost:" + (5000 + i) + "?client=netty"), new TelnetServerHandler());
+            aServer.close();
+        }
+    }
+
+    @AfterClass
+    public static void tearDown() throws Exception {
+        try {
+            if (server != null)
+                server.close();
+        } finally {}
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientToServerTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientToServerTest.java
new file mode 100644
index 0000000..ea42871
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientToServerTest.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * NettyClientToServerTest
+ * 
+ * @author william.liangf
+ */
+public class NettyClientToServerTest extends ClientToServerTest {
+
+    protected ExchangeServer newServer(int port, Replier<?> receiver) throws RemotingException {
+        return Exchangers.bind(URL.valueOf("exchange://localhost:" + port + "?server=netty"), receiver);
+    }
+
+    protected ExchangeChannel newClient(int port) throws RemotingException {
+        return Exchangers.connect(URL.valueOf("exchange://localhost:" + port + "?client=netty"));
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyStringTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyStringTest.java
new file mode 100644
index 0000000..dfb8405
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyStringTest.java
@@ -0,0 +1,64 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;

+

+import org.junit.AfterClass;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.remoting.exchange.Exchangers;

+

+/**

+ * User: heyman

+ * Date: 4/26/11

+ * Time: 4:13 PM

+ */

+public class NettyStringTest {

+    static ExchangeServer server;

+    static ExchangeChannel client;

+

+    @BeforeClass

+    public static void setUp() throws Exception {

+        //int port = (int) (1000 * Math.random() + 10000);

+        int port = 10001;

+        System.out.println(port);

+        server = Exchangers.bind(URL.valueOf("telnet://0.0.0.0:" + port + "?server=netty"), new TelnetServerHandler());

+        client = Exchangers.connect(URL.valueOf("telnet://127.0.0.1:" + port + "?client=netty"), new TelnetClientHandler());

+    }

+

+    @Test

+    public void testHandler() throws Exception {

+        //Thread.sleep(20000);

+        /*client.request("world\r\n");

+        Future future = client.request("world", 10000);

+        String result = (String)future.get();

+        Assert.assertEquals("Did you say 'world'?\r\n",result);*/

+    }

+

+    @AfterClass

+    public static void tearDown() throws Exception {

+        try {

+            if (server != null)

+                server.close();

+        } finally {

+            if (client != null)

+                client.close();

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetClientHandler.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetClientHandler.java
new file mode 100644
index 0000000..9f1aae5
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetClientHandler.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * User: heyman
+ * Date: 4/28/11
+ * Time: 11:15 AM
+ */
+public class TelnetClientHandler implements Replier<String> {
+
+    public Class<String> interest() {
+        return String.class;
+    }
+
+    public Object reply(ExchangeChannel channel, String msg) throws RemotingException {
+        return msg;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetServerHandler.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetServerHandler.java
new file mode 100644
index 0000000..ac5701b
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetServerHandler.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * User: heyman
+ * Date: 4/26/11
+ * Time: 4:29 PM
+ */
+public class TelnetServerHandler implements Replier<String> {
+
+    public Class<String> interest() {
+        return String.class;
+    }
+
+    public Object reply(ExchangeChannel channel, String msg) throws RemotingException {
+        // Generate and write a response.
+
+        String response;
+        if (msg.length() == 0) {
+            response = "Please type something.\r\n";
+        }  else {
+            response = "Did you say '" + msg + "'?\r\n";
+        }
+        //System.out.println(response);
+        return response;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/World.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/World.java
new file mode 100644
index 0000000..eca2cd85
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/World.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import java.io.Serializable;
+
+/**
+ * Data
+ * 
+ * @author william.liangf
+ */
+public class World implements Serializable {
+
+    private static final long serialVersionUID = 8563900571013747774L;
+    
+    private String name;
+    
+    public World() {
+    }
+
+    public World(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/WorldHandler.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/WorldHandler.java
new file mode 100644
index 0000000..4e257bc
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/WorldHandler.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * DataHandler
+ * 
+ * @author william.liangf
+ */
+public class WorldHandler implements Replier<World> {
+
+    public Class<World> interest() {
+        return World.class;
+    }
+
+    public Object reply(ExchangeChannel channel, World msg) throws RemotingException {
+        return new Hello("hello," + msg.getName());
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/resources/log4j.xml b/dubbo-remoting-netty/src/test/resources/log4j.xml
new file mode 100644
index 0000000..8e82064
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/resources/log4j.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+	<!-- ===================================================================== -->
+	<!-- 以下是appender的定义 -->
+	<!-- ===================================================================== -->
+	<appender name="dubbo" class="com.alibaba.dubbo.common.utils.DubboAppender">
+		<param name="encoding" value="GBK" />
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="%d %p [%c:%M] - %m%n" />
+		</layout>
+		<!-- <filter class="org.apache.log4j.varia.LevelRangeFilter">
+			<param name="LevelMin" value="DEBUG" />
+			<param name="LevelMax" value="DEBUG" />
+		</filter> -->
+	</appender>

+	<root>
+		<level value="INFO" />
+		<appender-ref ref="dubbo" />

+	</root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/pom.xml b/dubbo-remoting-p2p/pom.xml
new file mode 100644
index 0000000..5c416ab
--- /dev/null
+++ b/dubbo-remoting-p2p/pom.xml
@@ -0,0 +1,38 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-remoting-p2p</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo P2P Remoting Module</name>
+	<description>The p2p remoting module of dubbo project</description>

+	<properties>

+		<skip_maven_deploy>true</skip_maven_deploy>

+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Group.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Group.java
new file mode 100644
index 0000000..60d0031
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Group.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * Group. (SPI, Prototype, ThreadSafe)

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Peer-to-peer">Peer-to-peer</a>

+ * 

+ * @author william.liangf

+ */

+public interface Group {

+

+    /**

+     * get group url.

+     * 

+     * @return group url.

+     */

+    URL getUrl();

+

+    /**

+     * join.

+     * 

+     * @param url

+     */

+    Peer join(URL url, ChannelHandler handler) throws RemotingException;

+    

+    /**

+     * leave.

+     * 

+     * @param url

+     * @throws RemotingException

+     */

+    void leave(URL url) throws RemotingException;

+    

+    /**

+     * close the group.

+     */

+    void close();

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networker.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networker.java
new file mode 100644
index 0000000..5298348
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networker.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.SPI;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * Networker. (SPI, Singleton, ThreadSafe)

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Peer-to-peer">Peer-to-peer</a>

+ * 

+ * @author william.liangf

+ */

+@SPI

+public interface Networker {

+

+    /**

+     * lookup group.

+     * 

+     * @param url group url

+     * @return group.

+     */

+    Group lookup(URL url) throws RemotingException;

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networkers.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networkers.java
new file mode 100644
index 0000000..3df97f7
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networkers.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * Networkers. (API, Static, ThreadSafe)

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Peer-to-peer">Peer-to-peer</a>

+ * 

+ * @author william.liangf

+ */

+public class Networkers {

+    

+    public static Peer join(String group, String peer, ChannelHandler handler) throws RemotingException {

+        return join(URL.valueOf(group), URL.valueOf(peer), handler);

+    }

+

+    public static Peer join(URL group, URL peer, ChannelHandler handler) throws RemotingException {

+        return lookup(group).join(peer, handler);

+    }

+    

+    public static Group lookup(String group) throws RemotingException {

+        return lookup(URL.valueOf(group));

+    }

+    

+    public static Group lookup(URL group) throws RemotingException {

+        Networker networker = ExtensionLoader.getExtensionLoader(Networker.class).getExtension(group.getProtocol());

+        return networker.lookup(group);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Peer.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Peer.java
new file mode 100644
index 0000000..5f51174
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Peer.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p;

+

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+

+/**

+ * Peer. (SPI, Prototype, ThreadSafe)

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Peer-to-peer">Peer-to-peer</a>

+ * 

+ * @author william.liangf

+ */

+public interface Peer extends Server {

+    

+    /**

+     * leave.

+     * 

+     * @throws RemotingException

+     */

+    void leave() throws RemotingException;

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeGroup.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeGroup.java
new file mode 100644
index 0000000..a72587c
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeGroup.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.p2p.Group;

+

+/**

+ * Group

+ * 

+ * @author william.liangf

+ */

+public interface ExchangeGroup extends Group {

+

+    /**

+     * join.

+     * 

+     * @param url

+     */

+    ExchangePeer join(URL url, ExchangeHandler handler) throws RemotingException;

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworker.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworker.java
new file mode 100644
index 0000000..8437119
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworker.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * Networker

+ * 

+ * @author william.liangf

+ */

+public interface ExchangeNetworker {

+

+    /**

+     * lookup group.

+     * 

+     * @param url group url

+     * @return group.

+     */

+    ExchangeGroup lookup(URL url) throws RemotingException;

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworkers.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworkers.java
new file mode 100644
index 0000000..fcb0f3a
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworkers.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+

+/**

+ * Networkers

+ * 

+ * @author william.liangf

+ */

+public class ExchangeNetworkers {

+    

+    public static ExchangePeer join(String group, String peer, ExchangeHandler handler) throws RemotingException {

+        return join(URL.valueOf(group), URL.valueOf(peer), handler);

+    }

+

+    public static ExchangePeer join(URL group, URL peer, ExchangeHandler handler) throws RemotingException {

+        return lookup(group).join(peer, handler);

+    }

+    

+    public static ExchangeGroup lookup(String group) throws RemotingException {

+        return lookup(URL.valueOf(group));

+    }

+    

+    public static ExchangeGroup lookup(URL group) throws RemotingException {

+        ExchangeNetworker networker = ExtensionLoader.getExtensionLoader(ExchangeNetworker.class).getExtension(group.getProtocol());

+        return networker.lookup(group);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangePeer.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangePeer.java
new file mode 100644
index 0000000..9a68b48
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangePeer.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange;

+

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.remoting.p2p.Peer;

+

+/**

+ * Peer

+ * 

+ * @author william.liangf

+ */

+public interface ExchangePeer extends Peer, ExchangeServer {

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/AbstractExchangeGroup.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/AbstractExchangeGroup.java
new file mode 100644
index 0000000..d79ecd0
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/AbstractExchangeGroup.java
@@ -0,0 +1,124 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange.support;

+

+import java.util.ArrayList;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.remoting.exchange.Exchangers;

+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerDispatcher;

+import com.alibaba.dubbo.remoting.p2p.Peer;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeGroup;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangePeer;

+

+/**

+ * AbstractGroup

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractExchangeGroup implements ExchangeGroup {

+

+    // 日志输出

+    protected static final Logger logger = LoggerFactory.getLogger(AbstractExchangeGroup.class);

+    

+    protected final URL url;

+    

+    protected final Map<URL, ExchangeServer> servers = new ConcurrentHashMap<URL, ExchangeServer>();

+

+    protected final Map<URL, ExchangeClient> clients = new ConcurrentHashMap<URL, ExchangeClient>();

+    

+    protected final ExchangeHandlerDispatcher dispatcher = new ExchangeHandlerDispatcher();

+

+    public AbstractExchangeGroup(URL url){

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        this.url = url;

+    }

+    

+    public URL getUrl() {

+        return url;

+    }

+

+    public void close() {

+        for (URL url : new ArrayList<URL>(servers.keySet())) {

+            try {

+                leave(url);

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+        for (URL url : new ArrayList<URL>(clients.keySet())) {

+            try {

+                disconnect(url);

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+    }

+    

+    public Peer join(URL url, ChannelHandler handler) throws RemotingException {

+        return join(url, (ExchangeHandler) handler);

+    }

+    

+    public ExchangePeer join(URL url, ExchangeHandler handler) throws RemotingException {

+        ExchangeServer server = servers.get(url);

+        if (server == null) { // TODO 有并发间隙

+            server = Exchangers.bind(url, handler);

+            servers.put(url, server);

+            dispatcher.addChannelHandler(handler);

+        }

+        return new ExchangeServerPeer(server, clients, this);

+    }

+

+    public void leave(URL url) throws RemotingException {

+        Server server = servers.remove(url);

+        if (server != null) {

+            server.close();

+        }

+    }

+

+    protected Client connect(URL url) throws RemotingException {

+        if (servers.containsKey(url)) {

+            return null;

+        }

+        ExchangeClient client = clients.get(url);

+        if (client == null) { // TODO 有并发间隙

+            client = Exchangers.connect(url, dispatcher);

+            clients.put(url, client);

+        }

+        return client;

+    }

+

+    protected void disconnect(URL url) throws RemotingException {

+        Client client = clients.remove(url);

+        if (client != null) {

+            client.close();

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/ExchangeServerPeer.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/ExchangeServerPeer.java
new file mode 100644
index 0000000..5d59d99
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/ExchangeServerPeer.java
@@ -0,0 +1,137 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange.support;

+

+import java.net.InetSocketAddress;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.remoting.exchange.support.ExchangeServerDelegate;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeGroup;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangePeer;

+

+/**

+ * ServerPeer

+ * 

+ * @author william.liangf

+ */

+public class ExchangeServerPeer extends ExchangeServerDelegate implements ExchangePeer {

+    

+    private static final Logger logger = LoggerFactory.getLogger(ExchangeServerPeer.class);

+

+    private final Map<URL, ExchangeClient> clients;

+

+    private final ExchangeGroup group;

+    

+    public ExchangeServerPeer(ExchangeServer server, Map<URL, ExchangeClient> clients, ExchangeGroup group){

+        super(server);

+        this.clients = clients;

+        this.group = group;

+    }

+

+    public void leave() throws RemotingException {

+        group.leave(getUrl());

+    }

+

+    @Override

+    public void close() {

+        try {

+            leave();

+        } catch (RemotingException e) {

+            logger.error(e.getMessage(), e);

+        }

+    }

+    

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    @Override

+    public Collection<Channel> getChannels() {

+        return (Collection) getExchangeChannels();

+    }

+

+    @Override

+    public Channel getChannel(InetSocketAddress remoteAddress) {

+        return getExchangeChannel(remoteAddress);

+    }

+

+    @Override

+    public Collection<ExchangeChannel> getExchangeChannels() {

+        Collection<ExchangeChannel> channels = super.getExchangeChannels();

+        if (clients.size() > 0) {

+            channels = channels == null ? new ArrayList<ExchangeChannel>() : new ArrayList<ExchangeChannel>(channels);

+            channels.addAll(clients.values());

+        }

+        return channels;

+    }

+

+    @Override

+    public ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress) {

+        String host = remoteAddress.getAddress() != null ? remoteAddress.getAddress().getHostAddress() : remoteAddress.getHostName();

+        int port = remoteAddress.getPort();

+        ExchangeChannel channel = super.getExchangeChannel(remoteAddress);

+        if (channel == null) {

+            for (Map.Entry<URL, ExchangeClient> entry : clients.entrySet()) {

+                URL url = entry.getKey();

+                if (url.getIp().equals(host) && url.getPort() == port) {

+                    return entry.getValue();

+                }

+            }

+        }

+        return channel;

+    }

+

+    @Override

+    public void send(Object message) throws RemotingException {

+        send(message, getUrl().getParameter(Constants.SENT_KEY, false));

+    }

+

+    @Override

+    public void send(Object message, boolean sent) throws RemotingException {

+        Throwable last = null;

+        try {

+            super.send(message, sent);

+        } catch (Throwable t) {

+            last = t;

+        }

+        for (Client client : clients.values()) {

+            try {

+                client.send(message, sent);

+            } catch (Throwable t) {

+                last = t;

+            }

+        }

+        if (last != null) {

+            if (last instanceof RemotingException) {

+                throw (RemotingException) last;

+            } else if (last instanceof RuntimeException) {

+                throw (RuntimeException) last;

+            } else {

+                throw new RuntimeException(last.getMessage(), last);

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeGroup.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeGroup.java
new file mode 100644
index 0000000..8375534
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeGroup.java
@@ -0,0 +1,138 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange.support;

+

+import java.io.File;

+import java.io.IOException;

+import java.net.InetSocketAddress;

+import java.util.ArrayList;

+import java.util.List;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.IOUtils;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangePeer;

+

+/**

+ * FileGroup

+ * 

+ * @author william.liangf

+ */

+public class FileExchangeGroup extends AbstractExchangeGroup {

+    

+    private final File file;

+    

+    private volatile long last;

+

+    // 定时任务执行器

+    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3, new NamedThreadFactory("FileGroupModifiedChecker", true));

+

+    // 重连定时器,定时检查连接是否可用,不可用时,无限次重连

+    private final ScheduledFuture<?> checkModifiedFuture;

+

+    public FileExchangeGroup(URL url){

+        super(url);

+        String path = url.getHost() + "/" + url.getPath();

+        file = new File(path);

+        if (! file.exists()) {

+            throw new IllegalStateException("The group file not exists. file: " + path);

+        }

+        checkModifiedFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {

+            public void run() {

+                // 检测文件变更

+                try {

+                    check();

+                } catch (Throwable t) { // 防御性容错

+                    logger.error("Unexpected error occur at reconnect, cause: " + t.getMessage(), t);

+                }

+            }

+        }, 2000, 2000, TimeUnit.MILLISECONDS);

+    }

+

+    public void close() {

+        super.close();

+        try {

+            if (! checkModifiedFuture.isCancelled()) {

+                checkModifiedFuture.cancel(true);

+            }

+        } catch (Throwable t) {

+            logger.error(t.getMessage(), t);

+        }

+    }

+

+    private void check() throws RemotingException {

+        long modified = file.lastModified();

+        if (modified > last) {

+            last = modified;

+            changed();

+        }

+    }

+    

+    private void changed() throws RemotingException {

+        try {

+            String[] lines = IOUtils.readLines(file);

+            for (String line : lines) {

+                connect(URL.valueOf(line));

+            }

+        } catch (IOException e) {

+            throw new RemotingException(new InetSocketAddress(NetUtils.getLocalHost(), 0), getUrl().toInetSocketAddress(), e.getMessage(), e);

+        }

+    }

+

+    public ExchangePeer joinExchange(URL url, ExchangeHandler handler) throws RemotingException {

+        ExchangePeer peer = super.join(url, handler);

+        try {

+            String full = url.toFullString();

+            String[] lines = IOUtils.readLines(file);

+            for (String line : lines) {

+                if (full.equals(line)) {

+                    return peer;

+                }

+            }

+            IOUtils.appendLines(file, new String[] {full});

+        } catch (IOException e) {

+            throw new RemotingException(new InetSocketAddress(NetUtils.getLocalHost(), 0), getUrl().toInetSocketAddress(), e.getMessage(), e);

+        }

+        return peer;

+    }

+    

+    @Override

+    public void leave(URL url) throws RemotingException {

+        super.leave(url);

+        try {

+            String full = url.toFullString();

+            String[] lines = IOUtils.readLines(file);

+            List<String> saves = new ArrayList<String>();

+            for (String line : lines) {

+                if (full.equals(line)) {

+                    return;

+                }

+                saves.add(line);

+            }

+            IOUtils.appendLines(file, saves.toArray(new String[0]));

+        } catch (IOException e) {

+            throw new RemotingException(new InetSocketAddress(NetUtils.getLocalHost(), 0), getUrl().toInetSocketAddress(), e.getMessage(), e);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.java
new file mode 100644
index 0000000..85d7a1b
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange.support;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeGroup;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeNetworker;

+

+/**

+ * FileNetworker

+ * 

+ * @author william.liangf

+ */

+public class FileExchangeNetworker implements ExchangeNetworker {

+

+    public ExchangeGroup lookup(URL url) throws RemotingException {

+        return new FileExchangeGroup(url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java
new file mode 100644
index 0000000..3850261
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java
@@ -0,0 +1,120 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange.support;

+

+import java.io.IOException;

+import java.net.DatagramPacket;

+import java.net.InetAddress;

+import java.net.InetSocketAddress;

+import java.net.MulticastSocket;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangePeer;

+

+/**

+ * MulticastGroup

+ * 

+ * @author william.liangf

+ */

+public class MulticastExchangeGroup extends AbstractExchangeGroup {

+    

+    private static final String JOIN = "join";

+    

+    private static final String LEAVE = "leave";

+

+    private InetAddress mutilcastAddress;

+    

+    private MulticastSocket mutilcastSocket;

+

+    public MulticastExchangeGroup(URL url) {

+        super(url);

+        if (! isMulticastAddress(url.getHost())) {

+            throw new IllegalArgumentException("Invalid multicast address " + url.getHost() + ", scope: 224.0.0.0 - 239.255.255.255");

+        }

+        try {

+            mutilcastAddress = InetAddress.getByName(url.getHost());

+            mutilcastSocket = new MulticastSocket(url.getPort());

+            mutilcastSocket.setLoopbackMode(false);

+            mutilcastSocket.joinGroup(mutilcastAddress);

+            Thread thread = new Thread(new Runnable() {

+                public void run() {

+                    byte[] buf = new byte[1024];

+                    DatagramPacket recv = new DatagramPacket(buf, buf.length);

+                    while (true) {

+                        try {

+                            mutilcastSocket.receive(recv);

+                            MulticastExchangeGroup.this.receive(new String(recv.getData()).trim(), (InetSocketAddress) recv.getSocketAddress());

+                        } catch (Exception e) {

+                            logger.error(e.getMessage(), e);

+                        }

+                    }

+                }

+            }, "MulticastGroupReceiver");

+            thread.setDaemon(true);

+            thread.start();

+        } catch (IOException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+    

+    private static boolean isMulticastAddress(String ip) {

+        int i = ip.indexOf('.');

+        if (i > 0) {

+            String prefix = ip.substring(0, i);

+            if (StringUtils.isInteger(prefix)) {

+                int p = Integer.parseInt(prefix);

+                return p >= 224 && p <= 239;

+            }

+        }

+        return false;

+    }

+    

+    private void send(String msg) throws RemotingException {

+        DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(), mutilcastAddress, mutilcastSocket.getLocalPort());

+        try {

+            mutilcastSocket.send(hi);

+        } catch (IOException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+

+    private void receive(String msg, InetSocketAddress remoteAddress) throws RemotingException {

+        if (msg.startsWith(JOIN)) {

+            String url = msg.substring(JOIN.length()).trim();

+            connect(URL.valueOf(url));

+        } else if (msg.startsWith(LEAVE)) {

+            String url = msg.substring(LEAVE.length()).trim();

+            disconnect(URL.valueOf(url));

+        }

+    }

+    

+    @Override

+    public ExchangePeer join(URL url, ExchangeHandler handler) throws RemotingException {

+        ExchangePeer peer = super.join(url, handler);

+        send(JOIN + " " + url.toFullString());

+        return peer;

+    }

+

+    @Override

+    public void leave(URL url) throws RemotingException {

+        super.leave(url);

+        send(LEAVE + " " + url.toFullString());

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.java
new file mode 100644
index 0000000..f2345cd
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.exchange.support;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeGroup;

+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeNetworker;

+

+/**

+ * MulticastNetworker

+ * 

+ * @author william.liangf

+ */

+public class MulticastExchangeNetworker implements ExchangeNetworker {

+

+    public ExchangeGroup lookup(URL url) throws RemotingException {

+        return new MulticastExchangeGroup(url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/AbstractGroup.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/AbstractGroup.java
new file mode 100644
index 0000000..2fff7ba
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/AbstractGroup.java
@@ -0,0 +1,116 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.support;

+

+import java.util.ArrayList;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.Transporters;

+import com.alibaba.dubbo.remoting.p2p.Group;

+import com.alibaba.dubbo.remoting.p2p.Peer;

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerDispatcher;

+

+/**

+ * AbstractGroup

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractGroup implements Group {

+

+    // 日志输出

+    protected static final Logger logger = LoggerFactory.getLogger(AbstractGroup.class);

+    

+    protected final URL url;

+    

+    protected final Map<URL, Server> servers = new ConcurrentHashMap<URL, Server>();

+

+    protected final Map<URL, Client> clients = new ConcurrentHashMap<URL, Client>();

+    

+    protected final ChannelHandlerDispatcher dispatcher = new ChannelHandlerDispatcher();

+

+    public AbstractGroup(URL url){

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        this.url = url;

+    }

+    

+    public URL getUrl() {

+        return url;

+    }

+

+    public void close() {

+        for (URL url : new ArrayList<URL>(servers.keySet())) {

+            try {

+                leave(url);

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+        for (URL url : new ArrayList<URL>(clients.keySet())) {

+            try {

+                disconnect(url);

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+    }

+    

+    public Peer join(URL url, ChannelHandler handler) throws RemotingException {

+        Server server = servers.get(url);

+        if (server == null) { // TODO 有并发间隙

+            server = Transporters.bind(url, handler);

+            servers.put(url, server);

+            dispatcher.addChannelHandler(handler);

+        }

+        return new ServerPeer(server, clients, this);

+    }

+

+    public void leave(URL url) throws RemotingException {

+        Server server = servers.remove(url);

+        if (server != null) {

+            server.close();

+        }

+    }

+

+    protected Client connect(URL url) throws RemotingException {

+        if (servers.containsKey(url)) {

+            return null;

+        }

+        Client client = clients.get(url);

+        if (client == null) { // TODO 有并发间隙

+            client = Transporters.connect(url, dispatcher);

+            clients.put(url, client);

+        }

+        return client;

+    }

+

+    protected void disconnect(URL url) throws RemotingException {

+        Client client = clients.remove(url);

+        if (client != null) {

+            client.close();

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileGroup.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileGroup.java
new file mode 100644
index 0000000..1ad645b
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileGroup.java
@@ -0,0 +1,135 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.support;

+

+import java.io.File;

+import java.io.IOException;

+import java.net.InetSocketAddress;

+import java.util.ArrayList;

+import java.util.List;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.IOUtils;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.p2p.Peer;

+

+/**

+ * FileGroup

+ * 

+ * @author william.liangf

+ */

+public class FileGroup extends AbstractGroup {

+    

+    private final File file;

+    

+    private volatile long last;

+

+    // 定时任务执行器

+    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3, new NamedThreadFactory("FileGroupModifiedChecker", true));

+

+    // 重连定时器,定时检查连接是否可用,不可用时,无限次重连

+    private final ScheduledFuture<?> checkModifiedFuture;

+

+    public FileGroup(URL url){

+        super(url);

+        String path = url.getAbsolutePath();

+        file = new File(path);

+        checkModifiedFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {

+            public void run() {

+                // 检测文件变更

+                try {

+                    check();

+                } catch (Throwable t) { // 防御性容错

+                    logger.error("Unexpected error occur at reconnect, cause: " + t.getMessage(), t);

+                }

+            }

+        }, 2000, 2000, TimeUnit.MILLISECONDS);

+    }

+

+    public void close() {

+        super.close();

+        try {

+            if (! checkModifiedFuture.isCancelled()) {

+                checkModifiedFuture.cancel(true);

+            }

+        } catch (Throwable t) {

+            logger.error(t.getMessage(), t);

+        }

+    }

+

+    private void check() throws RemotingException {

+        long modified = file.lastModified();

+        if (modified > last) {

+            last = modified;

+            changed();

+        }

+    }

+    

+    private void changed() throws RemotingException {

+        try {

+            String[] lines = IOUtils.readLines(file);

+            for (String line : lines) {

+                connect(URL.valueOf(line));

+            }

+        } catch (IOException e) {

+            throw new RemotingException(new InetSocketAddress(NetUtils.getLocalHost(), 0), getUrl().toInetSocketAddress(), e.getMessage(), e);

+        }

+    }

+

+    public Peer join(URL url, ChannelHandler handler) throws RemotingException {

+        Peer peer = super.join(url, handler);

+        try {

+            String full = url.toFullString();

+            String[] lines = IOUtils.readLines(file);

+            for (String line : lines) {

+                if (full.equals(line)) {

+                    return peer;

+                }

+            }

+            IOUtils.appendLines(file, new String[] {full});

+        } catch (IOException e) {

+            throw new RemotingException(new InetSocketAddress(NetUtils.getLocalHost(), 0), getUrl().toInetSocketAddress(), e.getMessage(), e);

+        }

+        return peer;

+    }

+    

+    @Override

+    public void leave(URL url) throws RemotingException {

+        super.leave(url);

+        try {

+            String full = url.toFullString();

+            String[] lines = IOUtils.readLines(file);

+            List<String> saves = new ArrayList<String>();

+            for (String line : lines) {

+                if (full.equals(line)) {

+                    return;

+                }

+                saves.add(line);

+            }

+            IOUtils.appendLines(file, saves.toArray(new String[0]));

+        } catch (IOException e) {

+            throw new RemotingException(new InetSocketAddress(NetUtils.getLocalHost(), 0), getUrl().toInetSocketAddress(), e.getMessage(), e);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.java
new file mode 100644
index 0000000..1aa4f88
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.support;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.p2p.Group;

+import com.alibaba.dubbo.remoting.p2p.Networker;

+

+/**

+ * FileNetworker

+ * 

+ * @author william.liangf

+ */

+public class FileNetworker implements Networker {

+

+    public Group lookup(URL url) throws RemotingException {

+        return new FileGroup(url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastGroup.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastGroup.java
new file mode 100644
index 0000000..beca3ba
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastGroup.java
@@ -0,0 +1,120 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.support;

+

+import java.io.IOException;

+import java.net.DatagramPacket;

+import java.net.InetAddress;

+import java.net.InetSocketAddress;

+import java.net.MulticastSocket;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.p2p.Peer;

+

+/**

+ * MulticastGroup

+ * 

+ * @author william.liangf

+ */

+public class MulticastGroup extends AbstractGroup {

+    

+    private static final String JOIN = "join";

+    

+    private static final String LEAVE = "leave";

+

+    private InetAddress mutilcastAddress;

+    

+    private MulticastSocket mutilcastSocket;

+

+    public MulticastGroup(URL url) {

+        super(url);

+        if (! isMulticastAddress(url.getHost())) {

+            throw new IllegalArgumentException("Invalid multicast address " + url.getHost() + ", scope: 224.0.0.0 - 239.255.255.255");

+        }

+        try {

+            mutilcastAddress = InetAddress.getByName(url.getHost());

+            mutilcastSocket = new MulticastSocket(url.getPort());

+            mutilcastSocket.setLoopbackMode(false);

+            mutilcastSocket.joinGroup(mutilcastAddress);

+            Thread thread = new Thread(new Runnable() {

+                public void run() {

+                    byte[] buf = new byte[1024];

+                    DatagramPacket recv = new DatagramPacket(buf, buf.length);

+                    while (true) {

+                        try {

+                            mutilcastSocket.receive(recv);

+                            MulticastGroup.this.receive(new String(recv.getData()).trim(), (InetSocketAddress) recv.getSocketAddress());

+                        } catch (Exception e) {

+                            logger.error(e.getMessage(), e);

+                        }

+                    }

+                }

+            }, "MulticastGroupReceiver");

+            thread.setDaemon(true);

+            thread.start();

+        } catch (IOException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+    

+    private static boolean isMulticastAddress(String ip) {

+        int i = ip.indexOf('.');

+        if (i > 0) {

+            String prefix = ip.substring(0, i);

+            if (StringUtils.isInteger(prefix)) {

+                int p = Integer.parseInt(prefix);

+                return p >= 224 && p <= 239;

+            }

+        }

+        return false;

+    }

+    

+    private void send(String msg) throws RemotingException {

+        DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(), mutilcastAddress, mutilcastSocket.getLocalPort());

+        try {

+            mutilcastSocket.send(hi);

+        } catch (IOException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+

+    private void receive(String msg, InetSocketAddress remoteAddress) throws RemotingException {

+        if (msg.startsWith(JOIN)) {

+            String url = msg.substring(JOIN.length()).trim();

+            connect(URL.valueOf(url));

+        } else if (msg.startsWith(LEAVE)) {

+            String url = msg.substring(LEAVE.length()).trim();

+            disconnect(URL.valueOf(url));

+        }

+    }

+    

+    @Override

+    public Peer join(URL url, ChannelHandler handler) throws RemotingException {

+        Peer peer = super.join(url, handler);

+        send(JOIN + " " + url.toFullString());

+        return peer;

+    }

+

+    @Override

+    public void leave(URL url) throws RemotingException {

+        super.leave(url);

+        send(LEAVE + " " + url.toFullString());

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.java
new file mode 100644
index 0000000..4699f77
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.support;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.p2p.Group;

+import com.alibaba.dubbo.remoting.p2p.Networker;

+

+/**

+ * MulticastNetworker

+ * 

+ * @author william.liangf

+ */

+public class MulticastNetworker implements Networker {

+

+    public Group lookup(URL url) throws RemotingException {

+        return new MulticastGroup(url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/ServerPeer.java b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/ServerPeer.java
new file mode 100644
index 0000000..a1ff4da
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/ServerPeer.java
@@ -0,0 +1,124 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p.support;

+

+import java.net.InetSocketAddress;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.p2p.Group;

+import com.alibaba.dubbo.remoting.p2p.Peer;

+import com.alibaba.dubbo.remoting.transport.ServerDelegate;

+

+/**

+ * ServerPeer

+ * 

+ * @author william.liangf

+ */

+public class ServerPeer extends ServerDelegate implements Peer {

+    

+    private static final Logger logger = LoggerFactory.getLogger(ServerPeer.class);

+

+    private final Map<URL, Client> clients;

+

+    private final Group group;

+    

+    public ServerPeer(Server server, Map<URL, Client> clients, Group group){

+        super(server);

+        this.clients = clients;

+        this.group = group;

+    }

+

+    public void leave() throws RemotingException {

+        group.leave(getUrl());

+    }

+

+    @Override

+    public void close() {

+        try {

+            leave();

+        } catch (RemotingException e) {

+            logger.error(e.getMessage(), e);

+        }

+    }

+    

+    @Override

+    public Collection<Channel> getChannels() {

+        Collection<Channel> channels = super.getChannels();

+        if (clients.size() > 0) {

+            channels = channels == null ? new ArrayList<Channel>() : new ArrayList<Channel>(channels);

+            channels.addAll(clients.values());

+        }

+        return channels;

+    }

+

+    @Override

+    public Channel getChannel(InetSocketAddress remoteAddress) {

+        String host = remoteAddress.getAddress() != null ? remoteAddress.getAddress().getHostAddress() : remoteAddress.getHostName();

+        int port = remoteAddress.getPort();

+        Channel channel = super.getChannel(remoteAddress);

+        if (channel == null) {

+            for (Map.Entry<URL, Client> entry : clients.entrySet()) {

+                URL url = entry.getKey();

+                if (url.getIp().equals(host) && url.getPort() == port) {

+                    return entry.getValue();

+                }

+            }

+        }

+        return channel;

+    }

+

+    @Override

+    public void send(Object message) throws RemotingException {

+        send(message, getUrl().getParameter(Constants.SENT_KEY, false));

+    }

+

+    @Override

+    public void send(Object message, boolean sent) throws RemotingException {

+        Throwable last = null;

+        try {

+            super.send(message, sent);

+        } catch (Throwable t) {

+            last = t;

+        }

+        for (Client client : clients.values()) {

+            try {

+                client.send(message, sent);

+            } catch (Throwable t) {

+                last = t;

+            }

+        }

+        if (last != null) {

+            if (last instanceof RemotingException) {

+                throw (RemotingException) last;

+            } else if (last instanceof RuntimeException) {

+                throw (RuntimeException) last;

+            } else {

+                throw new RuntimeException(last.getMessage(), last);

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker b/dubbo-remoting-p2p/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker
new file mode 100644
index 0000000..521c6c0
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker
@@ -0,0 +1,2 @@
+multicast=com.alibaba.dubbo.remoting.p2p.support.MulticastNetworker

+file=com.alibaba.dubbo.remoting.p2p.support.FileNetworker
\ No newline at end of file
diff --git a/dubbo-remoting-p2p/src/test/java/com/alibaba/dubbo/remoting/p2p/PeerMain.java b/dubbo-remoting-p2p/src/test/java/com/alibaba/dubbo/remoting/p2p/PeerMain.java
new file mode 100644
index 0000000..de8d109
--- /dev/null
+++ b/dubbo-remoting-p2p/src/test/java/com/alibaba/dubbo/remoting/p2p/PeerMain.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.p2p;

+

+import java.util.Collection;

+

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;

+

+/**

+ * PeerMain

+ * 

+ * @author william.liangf

+ */

+public class PeerMain {

+    

+    public static void main(String[] args) throws Throwable {

+        String groupURL = "multicast://224.5.6.7:9911"; // 组地址,支持multicast和file两种组 ,可扩展

+        final String peerURL = "dubbo://0.0.0.0:" + (((int) (Math.random() * 10000)) + 20000); // 用于交叉组网的本机服务器地址

+        

+        // 加入组,并获取对等引用

+        Peer peer = Networkers.join(groupURL, peerURL, new ChannelHandlerAdapter(){

+            @Override

+            public void received(Channel channel, Object message) throws RemotingException {

+                System.out.println("Received: " + message + " in " + peerURL);

+            }

+        });

+        

+        // 向网络中存在的其它对等体发送消息

+        for (int i = 0; i < Integer.MAX_VALUE; i ++) {

+            Collection<Channel> channels = peer.getChannels(); // 获取与其它所有对等体的通道,此列表动态变化

+            if (channels != null && channels.size() > 0) {

+                for (Channel channel : channels) {

+                    channel.send("(" + i + ") " + peerURL); // 向指定对等体发送消息

+                }

+            }

+            Thread.sleep(1000);

+        }

+        

+        // 离开网络

+        peer.leave();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/pom.xml b/dubbo-remoting/pom.xml
new file mode 100644
index 0000000..74b0fc8
--- /dev/null
+++ b/dubbo-remoting/pom.xml
@@ -0,0 +1,38 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-remoting</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Remoting Module</name>
+	<description>The remoting module of dubbo project</description>

+	<properties>

+		<skip_maven_deploy>true</skip_maven_deploy>

+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-common</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Channel.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Channel.java
new file mode 100644
index 0000000..8a4e4bb
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Channel.java
@@ -0,0 +1,76 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import java.net.InetSocketAddress;
+
+/**
+ * Channel. (API/SPI, Prototype, ThreadSafe)
+ * 
+ * @see com.alibaba.dubbo.remoting.Client
+ * @see com.alibaba.dubbo.remoting.Server#getChannels()
+ * @see com.alibaba.dubbo.remoting.Server#getChannel(InetSocketAddress)
+ * @author qian.lei
+ * @author william.liangf
+ */
+public interface Channel extends Endpoint {
+
+    /**
+     * get remote address.
+     * 
+     * @return remote address.
+     */
+    InetSocketAddress getRemoteAddress();
+
+    /**
+     * is connected.
+     * 
+     * @return connected
+     */
+    boolean isConnected();
+
+    /**
+     * has attribute.
+     * 
+     * @param key key.
+     * @return has or has not.
+     */
+    boolean hasAttribute(String key);
+
+    /**
+     * get attribute.
+     * 
+     * @param key key.
+     * @return value.
+     */
+    Object getAttribute(String key);
+
+    /**
+     * set attribute.
+     * 
+     * @param key key.
+     * @param value value.
+     */
+    void setAttribute(String key,Object value);
+    
+    /**
+     * remove attribute.
+     * 
+     * @param key key.
+     */
+    void removeAttribute(String key);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.java
new file mode 100644
index 0000000..ee663ac
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.java
@@ -0,0 +1,70 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+

+import com.alibaba.dubbo.common.extension.SPI;

+
+
+/**
+ * ChannelHandler. (API, Prototype, ThreadSafe)
+ * 
+ * @see com.alibaba.dubbo.remoting.Transporter#bind(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @see com.alibaba.dubbo.remoting.Transporter#connect(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @author qian.lei
+ * @author william.liangf
+ */

+@SPI
+public interface ChannelHandler {
+
+    /**
+     * on channel connected.
+     * 
+     * @param channel channel.
+     */
+    void connected(Channel channel) throws RemotingException;
+
+    /**
+     * on channel disconnected.
+     * 
+     * @param channel channel.
+     */
+    void disconnected(Channel channel) throws RemotingException;
+
+    /**
+     * on message sent.
+     * 
+     * @param channel channel.
+     * @param message message.
+     */
+    void sent(Channel channel, Object message) throws RemotingException;
+
+    /**
+     * on message received.
+     * 
+     * @param channel channel.
+     * @param message message.
+     */
+    void received(Channel channel, Object message) throws RemotingException;
+
+    /**
+     * on exception caught.
+     * 
+     * @param channel channel.
+     * @param exception exception.
+     */
+    void caught(Channel channel, Throwable exception) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Client.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Client.java
new file mode 100644
index 0000000..f547b8a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Client.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import com.alibaba.dubbo.common.Resetable;

+

+/**

+ * Remoting Client. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Client%E2%80%93server_model">Client/Server</a>

+ * 

+ * @see com.alibaba.dubbo.remoting.Transporter#connect(com.alibaba.dubbo.common.URL, ChannelHandler)

+ * @author qian.lei

+ */

+public interface Client extends Endpoint, Channel, Resetable {

+

+    /**

+     * reconnect.

+     */

+    void reconnect() throws RemotingException;

+    

+    @Deprecated

+    void reset(com.alibaba.dubbo.common.Parameters parameters);

+    

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Codec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Codec.java
new file mode 100644
index 0000000..7145a93
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Codec.java
@@ -0,0 +1,64 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * Codec. (SPI, Singleton, ThreadSafe)
+ * 
+ * @author qianlei
+ * @author ding.lid
+ * @author william.liangf
+ */

+@SPI

+public interface Codec {
+
+	/**
+	 * Need more input poison.
+	 * 
+	 * @see #decode(Channel, InputStream)
+	 */
+	Object NEED_MORE_INPUT = new Object();
+
+    /**
+     * Encode message.
+     * 
+     * @param channel channel.
+     * @param output output stream.
+     * @param message message.
+     */
+	@Adaptive({Constants.CODEC_KEY})
+    void encode(Channel channel, OutputStream output, Object message) throws IOException;
+
+	/**
+	 * Decode message.
+	 * 
+	 * @see #NEED_MORE_INPUT
+	 * @param channel channel.
+	 * @param input input stream.
+	 * @return message or <code>NEED_MORE_INPUT</code> poison.
+	 */
+    @Adaptive({Constants.CODEC_KEY})
+	Object decode(Channel channel, InputStream input) throws IOException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Dispather.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Dispather.java
new file mode 100644
index 0000000..78fc371
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Dispather.java
@@ -0,0 +1,42 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+import com.alibaba.dubbo.remoting.transport.dispather.all.AllDispather;

+
+/**
+ * ChannelHandlerWrapper (SPI, Singleton, ThreadSafe)
+ * 
+ * @author chao.liuc
+ */
+@SPI(AllDispather.NAME)
+public interface Dispather {
+
+    /**
+     * dispath.
+     * 
+     * @param handler
+     * @param url
+     * @return channel handler
+     */
+    @Adaptive({Constants.DISPATHER_KEY, Constants.CHANNEL_HANDLER_KEY})
+    ChannelHandler dispath(ChannelHandler handler, URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Endpoint.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Endpoint.java
new file mode 100644
index 0000000..267ad30
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Endpoint.java
@@ -0,0 +1,86 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import java.net.InetSocketAddress;

+

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * Endpoint. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * @see com.alibaba.dubbo.remoting.Channel

+ * @see com.alibaba.dubbo.remoting.Client

+ * @see com.alibaba.dubbo.remoting.Server

+ * @author william.liangf

+ */

+public interface Endpoint {

+

+    /**

+     * get url.

+     * 

+     * @return url

+     */

+    URL getUrl();

+

+    /**

+     * get channel handler.

+     * 

+     * @return channel handler

+     */

+    ChannelHandler getChannelHandler();

+

+    /**

+     * get local address.

+     * 

+     * @return local address.

+     */

+    InetSocketAddress getLocalAddress();

+    

+    /**

+     * send message.

+     * 

+     * @param message

+     * @throws RemotingException

+     */

+    void send(Object message) throws RemotingException;

+

+    /**

+     * send message.

+     * 

+     * @param message

+     * @param sent 是否已发送完成

+     */

+    void send(Object message, boolean sent) throws RemotingException;

+

+    /**

+     * close the channel.

+     */

+    void close();

+    

+    /**

+     * Graceful close the channel.

+     */

+    void close(int timeout);

+    

+    /**

+     * is closed.

+     * 

+     * @return closed

+     */

+    boolean isClosed();

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ExecutionException.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ExecutionException.java
new file mode 100644
index 0000000..c54f6e4
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ExecutionException.java
@@ -0,0 +1,67 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import java.net.InetSocketAddress;

+

+/**

+ * ReceiveException

+ * 

+ * @author william.liangf

+ */

+public class ExecutionException extends RemotingException {

+    

+    private static final long serialVersionUID = -2531085236111056860L;

+    

+    private final Object request;

+

+    public ExecutionException(Object request, Channel channel, String message, Throwable cause){

+        super(channel, message, cause);

+        this.request = request;

+    }

+

+    public ExecutionException(Object request, Channel channel, String msg){

+        super(channel, msg);

+        this.request = request;

+    }

+

+    public ExecutionException(Object request, Channel channel, Throwable cause){

+        super(channel, cause);

+        this.request = request;

+    }

+

+    public ExecutionException(Object request, InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message,

+                            Throwable cause){

+        super(localAddress, remoteAddress, message, cause);

+        this.request = request;

+    }

+

+    public ExecutionException(Object request, InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message){

+        super(localAddress, remoteAddress, message);

+        this.request = request;

+    }

+

+    public ExecutionException(Object request, InetSocketAddress localAddress, InetSocketAddress remoteAddress, Throwable cause){

+        super(localAddress, remoteAddress, cause);

+        this.request = request;

+    }

+

+    

+    public Object getRequest() {

+        return request;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/RemotingException.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/RemotingException.java
new file mode 100644
index 0000000..7180b62
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/RemotingException.java
@@ -0,0 +1,84 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import java.net.InetSocketAddress;
+
+/**
+ * RemotingException. (API, Prototype, ThreadSafe)
+ * 
+ * @see com.alibaba.dubbo.remoting.exchange.ResponseFuture#get()
+ * @see com.alibaba.dubbo.remoting.exchange.ResponseFuture#get(int)
+ * @see com.alibaba.dubbo.remoting.Channel#send(Object, boolean)
+ * @see com.alibaba.dubbo.remoting.exchange.ExchangeChannel#request(Object)
+ * @see com.alibaba.dubbo.remoting.exchange.ExchangeChannel#request(Object, int)
+ * @see com.alibaba.dubbo.remoting.Transporter#bind(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @see com.alibaba.dubbo.remoting.Transporter#connect(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @author qian.lei
+ */
+public class RemotingException extends Exception {
+
+    private static final long serialVersionUID = -3160452149606778709L;
+
+    private InetSocketAddress localAddress;
+
+    private InetSocketAddress remoteAddress;
+
+    public RemotingException(Channel channel, String msg){
+        this(channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(),
+             msg);
+    }
+
+    public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message){
+        super(message);
+
+        this.localAddress = localAddress;
+        this.remoteAddress = remoteAddress;
+    }
+
+    public RemotingException(Channel channel, Throwable cause){
+        this(channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(),
+             cause);
+    }
+
+    public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, Throwable cause){
+        super(cause);
+
+        this.localAddress = localAddress;
+        this.remoteAddress = remoteAddress;
+    }
+
+    public RemotingException(Channel channel, String message, Throwable cause){
+        this(channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(),
+             message, cause);
+    }
+
+    public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message,
+                             Throwable cause){
+        super(message, cause);
+
+        this.localAddress = localAddress;
+        this.remoteAddress = remoteAddress;
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        return localAddress;
+    }
+
+    public InetSocketAddress getRemoteAddress() {
+        return remoteAddress;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Server.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Server.java
new file mode 100755
index 0000000..afe655a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Server.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import java.net.InetSocketAddress;

+import java.util.Collection;

+

+import com.alibaba.dubbo.common.Resetable;

+

+/**

+ * Remoting Server. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Client%E2%80%93server_model">Client/Server</a>

+ * 

+ * @see com.alibaba.dubbo.remoting.Transporter#bind(com.alibaba.dubbo.common.URL, ChannelHandler)

+ * @author qian.lei

+ */

+public interface Server extends Endpoint, Resetable {

+    

+    /**

+     * is bound.

+     * 

+     * @return bound

+     */

+    boolean isBound();

+

+    /**

+     * get channels.

+     * 

+     * @return channels

+     */

+    Collection<Channel> getChannels();

+

+    /**

+     * get channel.

+     * 

+     * @param remoteAddress

+     * @return channel

+     */

+    Channel getChannel(InetSocketAddress remoteAddress);

+

+    @Deprecated

+    void reset(com.alibaba.dubbo.common.Parameters parameters);

+    

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/TimeoutException.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/TimeoutException.java
new file mode 100644
index 0000000..1b20999
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/TimeoutException.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import java.net.InetSocketAddress;
+
+/**
+ * TimeoutException. (API, Prototype, ThreadSafe)
+ * 
+ * @see com.alibaba.dubbo.remoting.exchange.ResponseFuture#get()
+ * @see com.alibaba.dubbo.remoting.exchange.ResponseFuture#get(int)
+ * @author qian.lei
+ */
+public class TimeoutException extends RemotingException {
+
+    private static final long serialVersionUID = 3122966731958222692L;
+    
+    public static final int CLIENT_SIDE = 0;
+    
+    public static final int SERVER_SIDE = 1;
+
+    private final int       phase;
+
+    public TimeoutException(boolean serverSide, Channel channel, String message){
+        super(channel, message);
+        this.phase = serverSide ? SERVER_SIDE : CLIENT_SIDE;
+    }
+
+    public TimeoutException(boolean serverSide, InetSocketAddress localAddress, 
+                            InetSocketAddress remoteAddress, String message) {
+        super(localAddress, remoteAddress, message);
+        this.phase = serverSide ? SERVER_SIDE : CLIENT_SIDE;
+    }
+
+    public int getPhase() {
+        return phase;
+    }
+
+    public boolean isServerSide() {
+        return phase == 1;
+    }
+
+    public boolean isClientSide() {
+        return phase == 0;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporter.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporter.java
new file mode 100644
index 0000000..f832615
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporter.java
@@ -0,0 +1,62 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import javax.sound.midi.Receiver;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * Transporter. (SPI, Singleton, ThreadSafe)
+ * 
+ * <a href="http://en.wikipedia.org/wiki/Transport_Layer">Transport Layer</a>
+ * <a href="http://en.wikipedia.org/wiki/Client%E2%80%93server_model">Client/Server</a>
+ * 
+ * @see com.alibaba.dubbo.remoting.Transporters
+ * @author ding.lid
+ * @author william.liangf
+ */
+@SPI("netty")
+public interface Transporter {
+
+    /**
+     * Bind a server.
+     * 
+     * @see com.alibaba.dubbo.remoting.Transporters#bind(URL, Receiver, ChannelHandler)
+     * @param url server url
+     * @param handler
+     * @return server
+     * @throws RemotingException 
+     */
+    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
+    Server bind(URL url, ChannelHandler handler) throws RemotingException;
+
+    /**
+     * Connect to a server.
+     * 
+     * @see com.alibaba.dubbo.remoting.Transporters#connect(URL, Receiver, ChannelListener)
+     * @param url server url
+     * @param handler
+     * @return client
+     * @throws RemotingException 
+     */
+    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
+    Client connect(URL url, ChannelHandler handler) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporters.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporters.java
new file mode 100644
index 0000000..5f1d99c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporters.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerDispatcher;

+

+/**

+ * Transporter facade. (API, Static, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class Transporters {

+

+    public static Server bind(String url, ChannelHandler... handler) throws RemotingException {

+        return bind(URL.valueOf(url), handler);

+    }

+

+    public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        if (handlers == null || handlers.length == 0) {

+            throw new IllegalArgumentException("handlers == null");

+        }

+        ChannelHandler handler;

+        if (handlers.length == 1) {

+            handler = handlers[0];

+        } else {

+            handler = new ChannelHandlerDispatcher(handlers);

+        }

+        return getTransporter().bind(url, handler);

+    }

+

+    public static Client connect(String url, ChannelHandler... handler) throws RemotingException {

+        return connect(URL.valueOf(url), handler);

+    }

+

+    public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        ChannelHandler handler;

+        if (handlers == null || handlers.length == 0) {

+            handler = new ChannelHandlerAdapter();

+        } else if (handlers.length == 1) {

+            handler = handlers[0];

+        } else {

+            handler = new ChannelHandlerDispatcher(handlers);

+        }

+        return getTransporter().connect(url, handler);

+    }

+

+    public static Transporter getTransporter() {

+        return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();

+    }

+

+    static {

+        // check duplicate jar package

+        Version.checkDuplicate(Transporters.class);

+        Version.checkDuplicate(RemotingException.class);

+    }

+

+    private Transporters(){

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeChannel.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeChannel.java
new file mode 100644
index 0000000..b62cb2c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeChannel.java
@@ -0,0 +1,61 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;

+

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * ExchangeChannel. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public interface ExchangeChannel extends Channel {

+

+    /**

+     * send request.

+     * 

+     * @param request

+     * @return response future

+     * @throws RemotingException

+     */

+    ResponseFuture request(Object request) throws RemotingException;

+

+    /**

+     * send request.

+     * 

+     * @param request

+     * @param timeout

+     * @return response future

+     * @throws RemotingException

+     */

+    ResponseFuture request(Object request, int timeout) throws RemotingException;

+

+    /**

+     * get message handler.

+     * 

+     * @return message handler

+     */

+    ExchangeHandler getExchangeHandler();

+

+    /**

+     * graceful close.

+     * 

+     * @param timeout

+     */

+    void close(int timeout);

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeClient.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeClient.java
new file mode 100644
index 0000000..6e0d61c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeClient.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;

+

+import com.alibaba.dubbo.remoting.Client;

+

+/**

+ * ExchangeClient. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public interface ExchangeClient extends Client, ExchangeChannel {

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeHandler.java
new file mode 100644
index 0000000..b5e183b
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeHandler.java
@@ -0,0 +1,39 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;

+

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+

+/**

+ * ExchangeHandler. (API, Prototype, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public interface ExchangeHandler extends ChannelHandler, TelnetHandler {

+

+    /**

+     * reply.

+     * 

+     * @param channel

+     * @param request

+     * @return response

+     * @throws RemotingException

+     */

+    Object reply(ExchangeChannel channel, Object request) throws RemotingException;

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeServer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeServer.java
new file mode 100644
index 0000000..55ff1ba
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeServer.java
@@ -0,0 +1,45 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;

+

+import java.net.InetSocketAddress;

+import java.util.Collection;

+

+import com.alibaba.dubbo.remoting.Server;

+

+/**

+ * ExchangeServer. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public interface ExchangeServer extends Server {

+

+    /**

+     * get channels.

+     * 

+     * @return channels

+     */

+    Collection<ExchangeChannel> getExchangeChannels();

+

+    /**

+     * get channel.

+     * 

+     * @param remoteAddress

+     * @return channel

+     */

+    ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress);

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java
new file mode 100644
index 0000000..c303e42
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java
@@ -0,0 +1,56 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger;

+

+/**

+ * Exchanger. (SPI, Singleton, ThreadSafe)

+ * 

+ * <a href="http://en.wikipedia.org/wiki/Message_Exchange_Pattern">Message Exchange Pattern</a>

+ * <a href="http://en.wikipedia.org/wiki/Request-response">Request-Response</a>

+ * 

+ * @author william.liangf

+ */

+@SPI(HeaderExchanger.NAME)

+public interface Exchanger {

+

+    /**

+     * bind.

+     * 

+     * @param url

+     * @param handler

+     * @return message server

+     */

+    @Adaptive({Constants.EXCHANGER_KEY})

+    ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException;

+

+    /**

+     * connect.

+     * 

+     * @param url

+     * @param handler

+     * @return message channel

+     */

+    @Adaptive({Constants.EXCHANGER_KEY})

+    ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException;

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchangers.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchangers.java
new file mode 100644
index 0000000..4f6c21a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchangers.java
@@ -0,0 +1,122 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerDispatcher;

+import com.alibaba.dubbo.remoting.exchange.support.Replier;

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;

+

+/**

+ * Exchanger facade. (API, Static, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public class Exchangers {

+

+    public static ExchangeServer bind(String url, Replier<?> replier) throws RemotingException {

+        return bind(URL.valueOf(url), replier);

+    }

+

+    public static ExchangeServer bind(URL url,  Replier<?> replier) throws RemotingException {

+        return bind(url, new ChannelHandlerAdapter(), replier);

+    }

+

+    public static ExchangeServer bind(String url, ChannelHandler handler, Replier<?> replier) throws RemotingException {

+        return bind(URL.valueOf(url), handler, replier);

+    }

+

+    public static ExchangeServer bind(URL url, ChannelHandler handler, Replier<?> replier) throws RemotingException {

+        return bind(url, new ExchangeHandlerDispatcher(replier, handler));

+    }

+    

+    public static ExchangeServer bind(String url, ExchangeHandler handler) throws RemotingException {

+        return bind(URL.valueOf(url), handler);

+    }

+

+    public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        if (handler == null) {

+            throw new IllegalArgumentException("handler == null");

+        }

+        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");

+        return getExchanger(url).bind(url, handler);

+    }

+

+    public static ExchangeClient connect(String url) throws RemotingException {

+        return connect(URL.valueOf(url));

+    }

+

+    public static ExchangeClient connect(URL url) throws RemotingException {

+        return connect(url, new ChannelHandlerAdapter(), null);

+    }

+

+    public static ExchangeClient connect(String url, Replier<?> replier) throws RemotingException {

+        return connect(URL.valueOf(url), new ChannelHandlerAdapter(), replier);

+    }

+

+    public static ExchangeClient connect(URL url, Replier<?> replier) throws RemotingException {

+        return connect(url, new ChannelHandlerAdapter(), replier);

+    }

+

+    public static ExchangeClient connect(String url, ChannelHandler handler, Replier<?> replier) throws RemotingException {

+        return connect(URL.valueOf(url), handler, replier);

+    }

+

+    public static ExchangeClient connect(URL url, ChannelHandler handler, Replier<?> replier) throws RemotingException {

+        return connect(url, new ExchangeHandlerDispatcher(replier, handler));

+    }

+    

+    public static ExchangeClient connect(String url, ExchangeHandler handler) throws RemotingException {

+        return connect(URL.valueOf(url), handler);

+    }

+

+    public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        if (handler == null) {

+            throw new IllegalArgumentException("handler == null");

+        }

+        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");

+        return getExchanger(url).connect(url, handler);

+    }

+

+    public static Exchanger getExchanger(URL url) {

+        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);

+        return getExchanger(type);

+    }

+

+    public static Exchanger getExchanger(String type) {

+        return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);

+    }

+

+    static {

+        // check duplicate jar package

+        Version.checkDuplicate(Exchangers.class);

+    }

+

+    private Exchangers(){

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Request.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Request.java
new file mode 100644
index 0000000..c490a5f
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Request.java
@@ -0,0 +1,121 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Request.
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class Request {

+    

+    public static final String HEARTBEAT_EVENT = null;

+    

+    public static final String READONLY_EVENT = "R";

+    

+    private static final AtomicLong INVOKE_ID = new AtomicLong(0);
+
+    private final long    mId;
+
+    private String  mVersion;
+
+    private boolean mTwoWay   = true;

+    

+    private boolean mEvent = false;
+
+    private boolean mBroken   = false;
+
+    private Object  mData;
+
+    public Request() {
+        mId = newId();
+    }
+
+    public Request(long id){
+        mId = id;
+    }
+
+    public long getId() {
+        return mId;
+    }
+
+    public String getVersion() {
+        return mVersion;
+    }
+
+    public void setVersion(String version) {
+        mVersion = version;
+    }
+
+    public boolean isTwoWay() {
+        return mTwoWay;
+    }
+
+    public void setTwoWay(boolean twoWay) {
+        mTwoWay = twoWay;
+    }
+

+    public boolean isEvent() {

+        return mEvent;

+    }

+

+    public void setEvent(String event) {

+        mEvent = true;

+        mData = event;

+    }

+
+    public boolean isBroken() {
+        return mBroken;
+    }
+
+    public void setBroken(boolean mBroken) {
+        this.mBroken = mBroken;
+    }
+
+    public Object getData() {
+        return mData;
+    }
+
+    public void setData(Object msg) {
+        mData = msg;
+    }
+

+    public boolean isHeartbeat() {

+        return mEvent && HEARTBEAT_EVENT == mData;

+    }

+

+    @Deprecated

+    public void setHeartbeat(boolean isHeartbeat) {

+        if (isHeartbeat) {

+            setEvent(HEARTBEAT_EVENT);

+        }

+    }

+
+    private static long newId() {
+        // getAndIncrement()增长到MAX_VALUE时,再增长会变为MIN_VALUE,负数也可以做为ID
+        return INVOKE_ID.getAndIncrement();
+    }
+
+    @Override
+    public String toString() {
+        return "Request [id=" + mId + ", version=" + mVersion + ", twoway=" + mTwoWay + ", event=" + mEvent
+               + ", broken=" + mBroken + ", data=" + (mData == this ? "this" : mData) + "]";
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Response.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Response.java
new file mode 100644
index 0000000..0ac7070
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Response.java
@@ -0,0 +1,164 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;
+
+/**
+ * Response
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class Response {

+    

+    public static final String HEARTBEAT_EVENT = null;

+    

+    public static final String READONLY_EVENT = "R";
+
+    /**
+     * ok.
+     */
+    public static final byte OK                = 20;
+
+    /**
+     * clien side timeout.
+     */
+    public static final byte CLIENT_TIMEOUT    = 30;
+
+    /**
+     * server side timeout.
+     */
+    public static final byte SERVER_TIMEOUT    = 31;
+
+    /**
+     * request format error.
+     */
+    public static final byte BAD_REQUEST       = 40;
+

+    /**

+     * response format error.

+     */

+    public static final byte BAD_RESPONSE      = 50;

+
+    /**
+     * service not found.
+     */
+    public static final byte SERVICE_NOT_FOUND = 60;
+
+    /**
+     * service error.
+     */
+    public static final byte SERVICE_ERROR     = 70;
+
+    /**
+     * internal server error.
+     */
+    public static final byte SERVER_ERROR      = 80;
+
+    /**
+     * internal server error.
+     */
+    public static final byte CLIENT_ERROR      = 90;
+
+    private long             mId               = 0;
+
+    private String           mVersion;
+
+    private byte             mStatus           = OK;
+
+    private boolean          mEvent         = false;
+
+    private String           mErrorMsg;
+
+    private Object           mResult;
+
+    public Response(){
+    }
+
+    public Response(long id){
+        mId = id;
+    }
+
+    public Response(long id, String version){
+        mId = id;
+        mVersion = version;
+    }
+
+    public long getId() {
+        return mId;
+    }
+
+    public void setId(long id) {
+        mId = id;
+    }
+
+    public String getVersion() {
+        return mVersion;
+    }
+
+    public void setVersion(String version) {
+        mVersion = version;
+    }
+
+    public byte getStatus() {
+        return mStatus;
+    }
+
+    public void setStatus(byte status) {
+        mStatus = status;
+    }

+    

+    public boolean isEvent() {

+        return mEvent;

+    }

+

+    public void setEvent(String event) {

+        mEvent = true;

+        mResult = event;

+    }
+
+    public boolean isHeartbeat() {

+        return mEvent && HEARTBEAT_EVENT == mResult;

+    }

+

+    @Deprecated

+    public void setHeartbeat(boolean isHeartbeat) {

+        if (isHeartbeat) {

+            setEvent(HEARTBEAT_EVENT);

+        }

+    }
+
+    public Object getResult() {
+        return mResult;
+    }
+
+    public void setResult(Object msg) {
+        mResult = msg;
+    }
+
+    public String getErrorMessage() {
+        return mErrorMsg;
+    }
+
+    public void setErrorMessage(String msg) {
+        mErrorMsg = msg;
+    }
+
+    @Override
+    public String toString() {
+        return "Response [id=" + mId + ", version=" + mVersion + ", status=" + mStatus + ", event=" + mEvent
+               + ", error=" + mErrorMsg + ", result=" + (mResult == this ? "this" : mResult) + "]";
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java
new file mode 100644
index 0000000..efe3d8b
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java
@@ -0,0 +1,39 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;

+

+/**

+ * Callback

+ * 

+ * @author william.liangf

+ */

+public interface ResponseCallback {

+

+    /**

+     * done.

+     * 

+     * @param response

+     */

+    void done(Object response);

+

+    /**

+     * caught exception.

+     * 

+     * @param exception

+     */

+    void caught(Throwable exception);

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseFuture.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseFuture.java
new file mode 100644
index 0000000..69eb691
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseFuture.java
@@ -0,0 +1,59 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange;

+

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * Future. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * @see com.alibaba.dubbo.remoting.exchange.ExchangeChannel#request(Object)

+ * @see com.alibaba.dubbo.remoting.exchange.ExchangeChannel#request(Object, int)

+ * @author qian.lei

+ * @author william.liangf

+ */

+public interface ResponseFuture {

+

+    /**

+     * get result.

+     * 

+     * @return result.

+     */

+    Object get() throws RemotingException;

+

+    /**

+     * get result with the specified timeout.

+     * 

+     * @param timeoutInMillis timeout.

+     * @return result.

+     */

+    Object get(int timeoutInMillis) throws RemotingException;

+

+    /**

+     * set callback.

+     * 

+     * @param callback

+     */

+    void setCallback(ResponseCallback callback);

+

+    /**

+     * check is done.

+     * 

+     * @return done or not.

+     */

+    boolean isDone();

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java
new file mode 100644
index 0000000..181c34a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java
@@ -0,0 +1,433 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.codec;
+
+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.io.Bytes;

+import com.alibaba.dubbo.common.io.StreamUtils;

+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;

+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.dubbo.common.serialize.Serialization;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.exchange.Response;

+import com.alibaba.dubbo.remoting.exchange.support.DefaultFuture;

+import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec;

+
+/**
+ * ExchangeCodec.
+ * 
+ * @author qianlei
+ * @author william.liangf
+ */
+public class ExchangeCodec extends TelnetCodec {
+
+    private static final Logger     logger             = LoggerFactory.getLogger(ExchangeCodec.class);
+
+    // header length.
+    protected static final int      HEADER_LENGTH      = 16;
+
+    // magic header.
+    protected static final short    MAGIC              = (short) 0xdabb;
+    
+    protected static final byte     MAGIC_HIGH         = Bytes.short2bytes(MAGIC)[0];
+    
+    protected static final byte     MAGIC_LOW          = Bytes.short2bytes(MAGIC)[1];
+
+    // message flag.
+    protected static final byte     FLAG_REQUEST       = (byte) 0x80;
+
+    protected static final byte     FLAG_TWOWAY        = (byte) 0x40;
+
+    protected static final byte     FLAG_EVENT     = (byte) 0x20;
+
+    protected static final int      SERIALIZATION_MASK = 0x1f;
+
+    private static Map<Byte, Serialization> ID_SERIALIZATION_MAP   = new HashMap<Byte, Serialization>();
+    static {
+        Set<String> supportedExtensions = ExtensionLoader.getExtensionLoader(Serialization.class).getSupportedExtensions();
+        for (String name : supportedExtensions) {
+            Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name);
+            byte idByte = serialization.getContentTypeId();
+            if (ID_SERIALIZATION_MAP.containsKey(idByte)) {
+                logger.error("Serialization extension " + serialization.getClass().getName()
+                             + " has duplicate id to Serialization extension "
+                             + ID_SERIALIZATION_MAP.get(idByte).getClass().getName()
+                             + ", ignore this Serialization extension");
+                continue;
+            }
+            ID_SERIALIZATION_MAP.put(idByte, serialization);
+        }
+    }
+
+    public Short getMagicCode() {
+        return MAGIC;
+    }
+
+    public void encode(Channel channel, OutputStream os, Object msg) throws IOException {
+        if (msg instanceof Request) {
+            encodeRequest(channel, os, (Request) msg);
+        } else if (msg instanceof Response) {
+            encodeResponse(channel, os, (Response) msg);
+        } else {
+            super.encode(channel, os, msg);
+        }
+    }
+
+    public Object decode(Channel channel, InputStream is) throws IOException {
+        int readable = is.available();
+        byte[] header = new byte[Math.min(readable, HEADER_LENGTH)];
+        is.read(header);
+        return decode(channel, is, readable, header);
+    }
+    
+    protected Object decode(Channel channel, InputStream is, int readable, byte[] header) throws IOException {
+        // check magic number.
+        if (readable > 0 && header[0] != MAGIC_HIGH 
+                || readable > 1 && header[1] != MAGIC_LOW) {
+            int length = header.length;
+            if (header.length < readable) {
+                header = Bytes.copyOf(header, readable);
+                is.read(header, length, readable - length);
+            }
+            for (int i = 1; i < header.length - 1; i ++) {
+                if (header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) {
+                    UnsafeByteArrayInputStream bis = ((UnsafeByteArrayInputStream) is);
+                    bis.position(bis.position() - header.length + i);
+                    header = Bytes.copyOf(header, i);
+                    break;
+                }
+            }
+            return super.decode(channel, is, readable, header);
+        }
+        // check length.
+        if (readable < HEADER_LENGTH) {
+            return NEED_MORE_INPUT;
+        }
+
+        // get data length.
+        int len = Bytes.bytes2int(header, 12);
+        checkPayload(channel, len);
+
+        int tt = len + HEADER_LENGTH;
+        if( readable < tt ) {
+            return NEED_MORE_INPUT;
+        }
+
+        // limit input stream.
+        if( readable != tt )
+            is = StreamUtils.limitedInputStream(is, len);
+
+        byte flag = header[2], proto = (byte)( flag & SERIALIZATION_MASK );
+        Serialization s = getSerializationById(proto);
+        if (s == null) {
+            s = getSerialization(channel);
+        }
+        ObjectInput in = s.deserialize(channel.getUrl(), is);
+        // get request id.
+        long id = Bytes.bytes2long(header, 4);
+        if( ( flag & FLAG_REQUEST ) == 0 ) {
+            // decode response.
+            Response res = new Response(id);

+            if (( flag & FLAG_EVENT ) != 0){

+                res.setEvent(Response.HEARTBEAT_EVENT);

+            }
+            // get status.
+            byte status = header[3];
+            res.setStatus(status);
+            if( status == Response.OK ) {
+                try {
+                    Object data;
+                    if (res.isHeartbeat()) {

+                        data = decodeHeartbeatData(channel, in);

+                    } else if (res.isEvent()) {
+                        data = decodeEventData(channel, in);
+                    } else {
+                        data = decodeResponseData(channel, in, getRequestData(id));
+                    }
+                    res.setResult(data);
+                } catch (Throwable t) {
+                    res.setStatus(Response.CLIENT_ERROR);
+                    res.setErrorMessage(StringUtils.toString(t));
+                }
+            } else {
+                res.setErrorMessage(in.readUTF());
+            }
+            return res;
+        } else {
+            // decode request.
+            Request req = new Request(id);
+            req.setVersion("2.0.0");
+            req.setTwoWay( ( flag & FLAG_TWOWAY ) != 0 );

+            if (( flag & FLAG_EVENT ) != 0 ){

+                req.setEvent(Request.HEARTBEAT_EVENT);

+            }
+            try {
+                Object data;
+                if (req.isHeartbeat()) {

+                    data = decodeHeartbeatData(channel, in);

+                } else if (req.isEvent()) {
+                    data = decodeEventData(channel, in);
+                } else {
+                    data = decodeRequestData(channel, in);
+                }
+                req.setData(data);
+            } catch (Throwable t) {
+                // bad request
+                req.setBroken(true);
+                req.setData(t);
+            }
+            return req;
+        }
+    }
+

+    protected Object getRequestData(long id) {

+        DefaultFuture future = DefaultFuture.getFuture(id);

+        if (future == null)

+            return null;

+        Request req = future.getRequest();

+        if (req == null)

+            return null;

+        return req.getData();

+    }

+
+    protected void encodeRequest(Channel channel, OutputStream os, Request req) throws IOException {

+        Serialization serialization = getSerialization(channel);
+        // header.
+        byte[] header = new byte[HEADER_LENGTH];
+        // set magic number.
+        Bytes.short2bytes(MAGIC, header);
+
+        // set request and serialization flag.
+        header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
+
+        if (req.isTwoWay()) header[2] |= FLAG_TWOWAY;
+        if (req.isEvent()) header[2] |= FLAG_EVENT;
+
+        // set request id.
+        Bytes.long2bytes(req.getId(), header, 4);
+
+        // encode request data.
+        UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+        ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
+        if (req.isEvent()) {

+            encodeEventData(channel, out, req.getData());
+        } else {
+            encodeRequestData(channel, out, req.getData());
+        }
+        out.flushBuffer();
+        bos.flush();
+        bos.close();
+        byte[] data = bos.toByteArray();
+        Bytes.int2bytes(data.length, header, 12);
+
+        // write
+        os.write(header); // write header.
+        os.write(data); // write data.

+    }
+
+    protected void encodeResponse(Channel channel, OutputStream os, Response res) throws IOException {

+        try {
+            Serialization serialization = getSerialization(channel);
+            // header.
+            byte[] header = new byte[HEADER_LENGTH];
+            // set magic number.
+            Bytes.short2bytes(MAGIC, header);
+            // set request and serialization flag.
+            header[2] = serialization.getContentTypeId();
+            if (res.isHeartbeat()) header[2] |= FLAG_EVENT;
+            // set response status.
+            byte status = res.getStatus();
+            header[3] = status;
+            // set request id.
+            Bytes.long2bytes(res.getId(), header, 4);
+    
+            UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+            ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
+            // encode response data or error message.
+            if (status == Response.OK) {
+                if (res.isHeartbeat()) {

+                    encodeHeartbeatData(channel, out, res.getResult());
+                } else {
+                    encodeResponseData(channel, out, res.getResult());
+                }
+            }
+            else out.writeUTF(res.getErrorMessage());
+            out.flushBuffer();
+            bos.flush();
+            bos.close();
+    
+            byte[] data = bos.toByteArray();
+            Bytes.int2bytes(data.length, header, 12);
+            // write
+            os.write(header); // write header.
+            os.write(data); // write data.

+        } catch (Throwable t) {

+            // 发送失败信息给Consumer,否则Consumer只能等超时了

+            if (! res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) {

+                try {

+                    // FIXME 在Codec中打印出错日志?在IoHanndler的caught中统一处理?

+                    logger.warn("Fail to encode response: " + res + ", send bad_response info instead, cause: " + t.getMessage(), t);

+                    

+                    Response r = new Response(res.getId(), res.getVersion());

+                    r.setStatus(Response.BAD_RESPONSE);

+                    r.setErrorMessage("Failed to send response: " + res + ", cause: " + StringUtils.toString(t));

+                    channel.send(r);

+                    

+                    return;

+                } catch (RemotingException e) {

+                    logger.warn("Failed to send bad_response info back: " + res + ", cause: " + e.getMessage(), e);

+                }

+            }

+            

+            // 重新抛出收到的异常

+            if (t instanceof IOException) {

+                throw (IOException) t;

+            } else if (t instanceof RuntimeException) {

+                throw (RuntimeException) t;

+            } else if (t instanceof Error) {

+                throw (Error) t;

+            } else  {

+                throw new RuntimeException(t.getMessage(), t);

+            }

+        }
+    }

+    

+    private static final Serialization getSerializationById(Byte id) {
+        return ID_SERIALIZATION_MAP.get(id);
+    }
+    
+    @Override
+    protected Object decodeData(ObjectInput in) throws IOException {
+        return decodeRequestData(in);
+    }
+

+    @Deprecated
+    protected Object decodeHeartbeatData(ObjectInput in) throws IOException {
+        try {
+            return in.readObject();
+        } catch (ClassNotFoundException e) {
+            throw new IOException(StringUtils.toString("Read object failed.", e));
+        }
+    }
+
+    protected Object decodeRequestData(ObjectInput in) throws IOException {
+        try {
+            return in.readObject();
+        } catch (ClassNotFoundException e) {
+            throw new IOException(StringUtils.toString("Read object failed.", e));
+        }
+    }
+
+    protected Object decodeResponseData(ObjectInput in) throws IOException {
+        try {
+            return in.readObject();
+        } catch (ClassNotFoundException e) {
+            throw new IOException(StringUtils.toString("Read object failed.", e));
+        }
+    }
+    
+    @Override
+    protected void encodeData(ObjectOutput out, Object data) throws IOException {
+        encodeRequestData(out, data);
+    }
+    

+    private void encodeEventData(ObjectOutput out, Object data) throws IOException {

+        out.writeObject(data);

+    }

+    

+    @Deprecated
+    protected void encodeHeartbeatData(ObjectOutput out, Object data) throws IOException {
+        encodeEventData(out, data);
+    }
+
+    protected void encodeRequestData(ObjectOutput out, Object data) throws IOException {
+        out.writeObject(data);
+    }
+
+    protected void encodeResponseData(ObjectOutput out, Object data) throws IOException {
+        out.writeObject(data);
+    }
+    
+    @Override
+    protected Object decodeData(Channel channel, ObjectInput in) throws IOException {
+        return decodeRequestData(channel ,in);
+    }

+    

+    private Object decodeEventData(Channel channel, ObjectInput in) throws IOException {

+        try {

+            return in.readObject();

+        } catch (ClassNotFoundException e) {

+            throw new IOException(StringUtils.toString("Read object failed.", e));

+        }

+    }
+

+    @Deprecated
+    protected Object decodeHeartbeatData(Channel channel, ObjectInput in) throws IOException {
+        try {
+            return in.readObject();
+        } catch (ClassNotFoundException e) {
+            throw new IOException(StringUtils.toString("Read object failed.", e));
+        }
+    }
+
+    protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException {
+        return decodeRequestData(in);
+    }
+
+    protected Object decodeResponseData(Channel channel, ObjectInput in) throws IOException {
+        return decodeResponseData(in);
+    }
+

+    protected Object decodeResponseData(Channel channel, ObjectInput in, Object requestData) throws IOException {

+        return decodeResponseData(channel, in);

+    }

+    
+    @Override
+    protected void encodeData(Channel channel, ObjectOutput out, Object data) throws IOException {
+        encodeRequestData(channel, out, data);
+    }
+

+    private void encodeEventData(Channel channel, ObjectOutput out, Object data) throws IOException {

+        encodeEventData(out, data);

+    }

+    @Deprecated
+    protected void encodeHeartbeatData(Channel channel, ObjectOutput out, Object data) throws IOException {
+        encodeHeartbeatData(out, data);
+    }
+
+    protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {
+        encodeRequestData(out, data);
+    }
+
+    protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException {
+        encodeResponseData(out, data);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/DefaultFuture.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/DefaultFuture.java
new file mode 100644
index 0000000..d1bc6e4
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/DefaultFuture.java
@@ -0,0 +1,314 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support;
+
+import java.text.SimpleDateFormat;

+import java.util.Date;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.locks.Condition;

+import java.util.concurrent.locks.Lock;

+import java.util.concurrent.locks.ReentrantLock;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.TimeoutException;

+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.exchange.Response;

+import com.alibaba.dubbo.remoting.exchange.ResponseCallback;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+
+/**
+ * DefaultFuture.
+ * 
+ * @author qian.lei

+ * @author chao.liuc
+ */
+public class DefaultFuture implements ResponseFuture {
+
+    private static final Logger                   logger = LoggerFactory.getLogger(DefaultFuture.class);
+
+    private static final Map<Long, Channel>       CHANNELS   = new ConcurrentHashMap<Long, Channel>();
+
+    private static final Map<Long, DefaultFuture> FUTURES   = new ConcurrentHashMap<Long, DefaultFuture>();
+
+    // invoke id.
+    private final long                            id;
+
+    private final Channel                         channel;
+    
+    private final Request                         request;
+
+    private final int                             timeout;
+
+    private final Lock                            lock = new ReentrantLock();
+
+    private final Condition                       done = lock.newCondition();
+
+    private final long                            start = System.currentTimeMillis();
+
+    private volatile long                         sent;
+    
+    private volatile Response                     response;
+
+    private volatile ResponseCallback             callback;
+
+    public DefaultFuture(Channel channel, Request request, int timeout){
+        this.channel = channel;
+        this.request = request;
+        this.id = request.getId();
+        this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);

+        // put into waiting map.
+        FUTURES.put(id, this);
+        CHANNELS.put(id, channel);
+    }
+    
+    public Object get() throws RemotingException {
+        return get(timeout);
+    }
+
+    public Object get(int timeout) throws RemotingException {
+        if (timeout <= 0) {
+            timeout = Constants.DEFAULT_TIMEOUT;
+        }
+        if (! isDone()) {
+            long start = System.currentTimeMillis();
+            lock.lock();
+            try {
+                while (! isDone()) {
+                    done.await(timeout, TimeUnit.MILLISECONDS);
+                    if (isDone() || System.currentTimeMillis() - start > timeout) {
+                        break;
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            } finally {
+                lock.unlock();
+            }
+            if (! isDone()) {
+                throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
+            }
+        }
+        return returnFromResponse();
+    }
+    
+    public void cancel(){
+        Response errorResult = new Response(id);
+        errorResult.setErrorMessage("request future has been canceled.");

+        response = errorResult ;
+        FUTURES.remove(id);
+        CHANNELS.remove(id);
+    }
+
+    public boolean isDone() {
+        return response != null;
+    }
+
+    public void setCallback(ResponseCallback callback) {

+        if (isDone()) {

+            invokeCallback(callback);
+        } else {

+            boolean isdone = false;

+            lock.lock();

+            try{

+                if (!isDone()) {

+                    this.callback = callback;

+                } else {

+                    isdone = true;

+                }

+            }finally {

+                lock.unlock();

+            }

+            if (isdone){

+                invokeCallback(callback);

+            }

+        }
+    }

+    private void invokeCallback(ResponseCallback c){

+        ResponseCallback callbackCopy = c;

+        if (callbackCopy == null){

+            throw new NullPointerException("callback cannot be null.");

+        }

+        c = null;

+        Response res = response;

+        if (res == null) {

+            throw new IllegalStateException("response cannot be null. url:"+channel.getUrl());

+        }

+        

+        if (res.getStatus() == Response.OK) {

+            try {

+                callbackCopy.done(res.getResult());

+            } catch (Exception e) {

+                logger.error("callback invoke error .reasult:" + res.getResult() + ",url:" + channel.getUrl(), e);

+            }

+        } else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {

+            try {

+                TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());

+                callbackCopy.caught(te);

+            } catch (Exception e) {

+                logger.error("callback invoke error ,url:" + channel.getUrl(), e);

+            }

+        } else {

+            try {

+                RuntimeException re = new RuntimeException(res.getErrorMessage());

+                callbackCopy.caught(re);

+            } catch (Exception e) {

+                logger.error("callback invoke error ,url:" + channel.getUrl(), e);

+            }

+        }

+    }
+
+    private Object returnFromResponse() throws RemotingException {

+        Response res = response;

+        if (res == null) {

+            throw new IllegalStateException("response cannot be null");

+        }

+        if (res.getStatus() == Response.OK) {

+            return res.getResult();

+        }

+        if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {

+            throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());

+        }

+        throw new RemotingException(channel, res.getErrorMessage());

+    }

+

+    private long getId() {
+        return id;
+    }
+    
+    private Channel getChannel() {
+        return channel;
+    }
+    
+    private boolean isSent() {
+        return sent > 0;
+    }
+
+    public Request getRequest() {
+        return request;
+    }
+
+    private int getTimeout() {
+        return timeout;
+    }
+
+    private long getStartTimestamp() {
+        return start;
+    }
+

+    public static DefaultFuture getFuture(long id) {

+        return FUTURES.get(id);

+    }

+
+    public static boolean hasFuture(Channel channel) {
+        return CHANNELS.containsValue(channel);
+    }
+
+    public static void sent(Channel channel, Request request) {
+        DefaultFuture future = FUTURES.get(request.getId());
+        if (future != null) {
+            future.doSent();
+        }
+    }
+
+    private void doSent() {
+        sent = System.currentTimeMillis();
+    }
+
+    public static void received(Channel channel, Response response) {
+        try {
+            DefaultFuture future = FUTURES.remove(response.getId());
+            if (future != null) {
+                future.doReceived(response);
+            } else {
+                logger.warn("The timeout response finally returned at " 
+                            + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) 
+                            + ", response " + response 
+                            + (channel == null ? "" : ", channel: " + channel.getLocalAddress() 
+                                + " -> " + channel.getRemoteAddress()));
+            }
+        } finally {
+            CHANNELS.remove(response.getId());
+        }
+    }
+
+    private void doReceived(Response res) {
+        lock.lock();
+        try {
+            response = res;
+            if (done != null) {
+                done.signal();
+            }
+        } finally {
+            lock.unlock();
+        }
+        if (callback != null) {

+            invokeCallback(callback);
+        }
+    }
+
+    private String getTimeoutMessage(boolean scan) {
+        long nowTimestamp = System.currentTimeMillis();
+        return (sent > 0 ? "Waiting server-side response timeout" : "Sending request timeout in client-side")
+                    + (scan ? " by scan timer" : "") + ". start time: " 
+                    + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(start))) + ", end time: " 
+                    + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ","
+                    + (sent > 0 ? " client elapsed: " + (sent - start) 
+                        + " ms, server elapsed: " + (nowTimestamp - sent)
+                        : " elapsed: " + (nowTimestamp - start)) + " ms, timeout: "
+                    + timeout + " ms, request: " + request + ", channel: " + channel.getLocalAddress()
+                    + " -> " + channel.getRemoteAddress();
+    }
+
+    private static class RemotingInvocationTimeoutScan implements Runnable {
+
+        public void run() {
+            while (true) {
+                try {
+                    for (DefaultFuture future : FUTURES.values()) {
+                        if (future == null || future.isDone()) {
+                            continue;
+                        }
+                        if (System.currentTimeMillis() - future.getStartTimestamp() > future.getTimeout()) {
+                            // create exception response.
+                            Response timeoutResponse = new Response(future.getId());
+                            // set timeout status.
+                            timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
+                            timeoutResponse.setErrorMessage(future.getTimeoutMessage(true));
+                            // handle response.
+                            DefaultFuture.received(future.getChannel(), timeoutResponse);
+                        }
+                    }
+                    Thread.sleep(30);
+                } catch (Throwable e) {
+                    logger.error("Exception when scan the timeout invocation of remoting.", e);
+                }
+            }
+        }
+    }
+
+    static {
+        Thread th = new Thread(new RemotingInvocationTimeoutScan(), "DubboResponseTimeoutScanTimer");
+        th.setDaemon(true);
+        th.start();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerAdapter.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerAdapter.java
new file mode 100644
index 0000000..378d7fb
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerAdapter.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support;

+

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.telnet.support.TelnetHandlerAdapter;

+

+/**

+ * ExchangeHandlerAdapter

+ * 

+ * @author william.liangf

+ */

+public abstract class ExchangeHandlerAdapter extends TelnetHandlerAdapter implements ExchangeHandler {

+

+    public Object reply(ExchangeChannel channel, Object msg) throws RemotingException {

+        return null;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java
new file mode 100644
index 0000000..895b190
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java
@@ -0,0 +1,113 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support;

+

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.TelnetHandlerAdapter;

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerDispatcher;

+

+/**

+ * ExchangeHandlerDispatcher

+ * 

+ * @author william.liangf

+ */

+public class ExchangeHandlerDispatcher implements ExchangeHandler {

+

+    private final ReplierDispatcher replierDispatcher;

+

+    private final ChannelHandlerDispatcher handlerDispatcher;

+

+    private final TelnetHandler telnetHandler;

+    

+    public ExchangeHandlerDispatcher() {

+        replierDispatcher = new ReplierDispatcher();

+        handlerDispatcher = new ChannelHandlerDispatcher();

+        telnetHandler = new TelnetHandlerAdapter();

+    }

+    

+    public ExchangeHandlerDispatcher(Replier<?> replier){

+        replierDispatcher = new ReplierDispatcher(replier);

+        handlerDispatcher = new ChannelHandlerDispatcher();

+        telnetHandler = new TelnetHandlerAdapter();

+    }

+    

+    public ExchangeHandlerDispatcher(ChannelHandler... handlers){

+        replierDispatcher = new ReplierDispatcher();

+        handlerDispatcher = new ChannelHandlerDispatcher(handlers);

+        telnetHandler = new TelnetHandlerAdapter();

+    }

+    

+    public ExchangeHandlerDispatcher(Replier<?> replier, ChannelHandler... handlers){

+        replierDispatcher = new ReplierDispatcher(replier);

+        handlerDispatcher = new ChannelHandlerDispatcher(handlers);

+        telnetHandler = new TelnetHandlerAdapter();

+    }

+

+    public ExchangeHandlerDispatcher addChannelHandler(ChannelHandler handler) {

+        handlerDispatcher.addChannelHandler(handler);

+        return this;

+    }

+

+    public ExchangeHandlerDispatcher removeChannelHandler(ChannelHandler handler) {

+        handlerDispatcher.removeChannelHandler(handler);

+        return this;

+    }

+

+    public <T> ExchangeHandlerDispatcher addReplier(Class<T> type, Replier<T> replier) {

+        replierDispatcher.addReplier(type, replier);

+        return this;

+    }

+

+    public <T> ExchangeHandlerDispatcher removeReplier(Class<T> type) {

+        replierDispatcher.removeReplier(type);

+        return this;

+    }

+

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public Object reply(ExchangeChannel channel, Object request) throws RemotingException {

+        return ((Replier)replierDispatcher).reply(channel, request);

+    }

+

+    public void connected(Channel channel) {

+        handlerDispatcher.connected(channel);

+    }

+

+    public void disconnected(Channel channel) {

+        handlerDispatcher.disconnected(channel);

+    }

+

+    public void sent(Channel channel, Object message) {

+        handlerDispatcher.sent(channel, message);

+    }

+

+    public void received(Channel channel, Object message) {

+        handlerDispatcher.received(channel, message);

+    }

+

+    public void caught(Channel channel, Throwable exception) {

+        handlerDispatcher.caught(channel, exception);

+    }

+

+    public String telnet(Channel channel, String message) throws RemotingException {

+        return telnetHandler.telnet(channel, message);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeServerDelegate.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeServerDelegate.java
new file mode 100644
index 0000000..b15efcd
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeServerDelegate.java
@@ -0,0 +1,113 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support;

+

+import java.net.InetSocketAddress;

+import java.util.Collection;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+

+/**

+ * ExchangeServerDelegate

+ * 

+ * @author william.liangf

+ */

+public class ExchangeServerDelegate implements ExchangeServer {

+    

+    private transient ExchangeServer server;

+    

+    public ExchangeServerDelegate() {

+    }

+

+    public ExchangeServerDelegate(ExchangeServer server){

+        setServer(server);

+    }

+

+    public ExchangeServer getServer() {

+        return server;

+    }

+    

+    public void setServer(ExchangeServer server) {

+        this.server = server;

+    }

+

+    public boolean isBound() {

+        return server.isBound();

+    }

+

+    public void reset(URL url) {

+        server.reset(url);

+    }

+

+    @Deprecated

+    public void reset(com.alibaba.dubbo.common.Parameters parameters){

+        reset(getUrl().addParameters(parameters.getParameters()));

+    }

+    

+    public Collection<Channel> getChannels() {

+        return server.getChannels();

+    }

+

+    public Channel getChannel(InetSocketAddress remoteAddress) {

+        return server.getChannel(remoteAddress);

+    }

+

+    public URL getUrl() {

+        return server.getUrl();

+    }

+

+    public ChannelHandler getChannelHandler() {

+        return server.getChannelHandler();

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return server.getLocalAddress();

+    }

+

+    public void send(Object message) throws RemotingException {

+        server.send(message);

+    }

+

+    public void send(Object message, boolean sent) throws RemotingException {

+        server.send(message, sent);

+    }

+

+    public void close() {

+        server.close();

+    }

+

+    public boolean isClosed() {

+        return server.isClosed();

+    }

+

+    public Collection<ExchangeChannel> getExchangeChannels() {

+        return server.getExchangeChannels();

+    }

+

+    public ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress) {

+        return server.getExchangeChannel(remoteAddress);

+    }

+

+    public void close(int timeout) {

+        server.close(timeout);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/Replier.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/Replier.java
new file mode 100755
index 0000000..01a3c95
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/Replier.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support;

+

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+

+/**

+ * Replier. (API, Prototype, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+public interface Replier<T> {

+

+    /**

+     * reply.

+     * 

+     * @param channel

+     * @param request

+     * @return response

+     * @throws RemotingException

+     */

+    Object reply(ExchangeChannel channel, T request) throws RemotingException;

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ReplierDispatcher.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ReplierDispatcher.java
new file mode 100644
index 0000000..8a7ff7c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ReplierDispatcher.java
@@ -0,0 +1,77 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support;

+

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+

+/**

+ * ReplierDispatcher

+ * 

+ * @author william.liangf

+ */

+public class ReplierDispatcher implements Replier<Object> {

+

+    private final Replier<?> defaultReplier;

+    

+    private final Map<Class<?>, Replier<?>> repliers = new ConcurrentHashMap<Class<?>, Replier<?>>();

+

+    public ReplierDispatcher(){

+        this(null, null);

+    }

+    

+    public ReplierDispatcher(Replier<?> defaultReplier){

+        this(defaultReplier, null);

+    }

+

+    public ReplierDispatcher(Replier<?> defaultReplier, Map<Class<?>, Replier<?>> repliers){

+        this.defaultReplier = defaultReplier;

+        if (repliers != null && repliers.size() > 0) {

+            this.repliers.putAll(repliers);

+        }

+    }

+

+    public <T> ReplierDispatcher addReplier(Class<T> type, Replier<T> replier) {

+        repliers.put(type, replier);

+        return this;

+    }

+

+    public <T> ReplierDispatcher removeReplier(Class<T> type) {

+        repliers.remove(type);

+        return this;

+    }

+

+    private Replier<?> getReplier(Class<?> type) {

+        for(Map.Entry<Class<?>, Replier<?>> entry : repliers.entrySet()) {

+            if(entry.getKey().isAssignableFrom(type)) {

+                return entry.getValue();

+            }

+        }

+        if (defaultReplier != null) {

+            return defaultReplier;

+        }

+        throw new IllegalStateException("Replier not found, Unsupported message object: " + type);

+    }

+

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public Object reply(ExchangeChannel channel, Object request) throws RemotingException {

+        return ((Replier)getReplier(request.getClass())).reply(channel, request);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/SimpleFuture.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/SimpleFuture.java
new file mode 100644
index 0000000..c09a0f0
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/SimpleFuture.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support;

+

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ResponseCallback;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+

+/**

+ * SimpleFuture

+ * 

+ * @author william.liangf

+ */

+public class SimpleFuture implements ResponseFuture {

+    

+    private final Object value;

+

+    public SimpleFuture(Object value){

+        this.value = value;

+    }

+

+    public Object get() throws RemotingException {

+        return value;

+    }

+

+    public Object get(int timeoutInMillis) throws RemotingException {

+        return value;

+    }

+

+    public void setCallback(ResponseCallback callback) {

+        callback.done(value);

+    }

+

+    public boolean isDone() {

+        return true;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java
new file mode 100644
index 0000000..aa8341b
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java
@@ -0,0 +1,217 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support.header;

+

+import java.net.InetSocketAddress;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.exchange.Response;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+import com.alibaba.dubbo.remoting.exchange.support.DefaultFuture;

+

+/**

+ * ExchangeReceiver

+ * 

+ * @author william.liangf

+ */

+final class HeaderExchangeChannel implements ExchangeChannel {

+

+    private static final Logger logger      = LoggerFactory.getLogger(HeaderExchangeChannel.class);

+

+    private static final String CHANNEL_KEY = HeaderExchangeChannel.class.getName() + ".CHANNEL";

+

+    private final Channel       channel;

+

+    private volatile boolean    closed      = false;

+

+    HeaderExchangeChannel(Channel channel){

+        if (channel == null) {

+            throw new IllegalArgumentException("channel == null");

+        }

+        this.channel = channel;

+    }

+

+    static HeaderExchangeChannel getOrAddChannel(Channel ch) {

+        if (ch == null) {

+            return null;

+        }

+        HeaderExchangeChannel ret = (HeaderExchangeChannel) ch.getAttribute(CHANNEL_KEY);

+        if (ret == null) {

+            ret = new HeaderExchangeChannel(ch);

+            if (ch.isConnected()) {

+                ch.setAttribute(CHANNEL_KEY, ret);

+            }

+        }

+        return ret;

+    }

+    

+    static void removeChannelIfDisconnected(Channel ch) {

+        if (ch != null && ! ch.isConnected()) {

+            ch.removeAttribute(CHANNEL_KEY);

+        }

+    }

+    

+    public void send(Object message) throws RemotingException {

+        send(message, getUrl().getParameter(Constants.SENT_KEY, false));

+    }

+    

+    public void send(Object message, boolean sent) throws RemotingException {

+        if (closed) {

+            throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The channel " + this + " is closed!");

+        }

+        if (message instanceof Request

+                || message instanceof Response

+                || message instanceof String) {

+            channel.send(message, sent);

+        } else {

+            Request request = new Request();

+            request.setVersion("2.0.0");

+            request.setTwoWay(false);

+            request.setData(message);

+            channel.send(request, sent);

+        }

+    }

+

+    public ResponseFuture request(Object request) throws RemotingException {

+        return request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));

+    }

+

+    public ResponseFuture request(Object request, int timeout) throws RemotingException {

+        if (closed) {

+            throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");

+        }

+        // create request.

+        Request req = new Request();

+        req.setVersion("2.0.0");

+        req.setTwoWay(true);

+        req.setData(request);

+        DefaultFuture future = new DefaultFuture(channel, req, timeout);

+        try{

+            channel.send(req);

+        }catch (RemotingException e) {

+            future.cancel();

+            throw e;

+        }

+        return future;

+    }

+

+    public boolean isClosed() {

+        return closed;

+    }

+

+    public void close() {

+        try {

+            channel.close();

+        } catch (Throwable e) {

+            logger.warn(e.getMessage(), e);

+        }

+    }

+

+    // graceful close

+    public void close(int timeout) {

+        if (closed) {

+            return;

+        }

+        closed = true;

+        if (timeout > 0) {

+            long start = System.currentTimeMillis();

+            while (DefaultFuture.hasFuture(HeaderExchangeChannel.this) 

+                    && System.currentTimeMillis() - start < timeout) {

+                try {

+                    Thread.sleep(10);

+                } catch (InterruptedException e) {

+                    logger.warn(e.getMessage(), e);

+                }

+            }

+        }

+        close();

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return channel.getLocalAddress();

+    }

+

+    public InetSocketAddress getRemoteAddress() {

+        return channel.getRemoteAddress();

+    }

+

+    public URL getUrl() {

+        return channel.getUrl();

+    }

+

+    public boolean isConnected() {

+        return channel.isConnected();

+    }

+

+    public ChannelHandler getChannelHandler() {

+        return channel.getChannelHandler();

+    }

+

+    public ExchangeHandler getExchangeHandler() {

+        return (ExchangeHandler) channel.getChannelHandler();

+    }

+    

+    public Object getAttribute(String key) {

+        return channel.getAttribute(key);

+    }

+

+    public void setAttribute(String key, Object value) {

+        channel.setAttribute(key, value);

+    }

+

+    public void removeAttribute(String key) {

+        channel.removeAttribute(key);

+    }

+

+    public boolean hasAttribute(String key) {

+        return channel.hasAttribute(key);

+    }

+

+    @Override

+    public int hashCode() {

+        final int prime = 31;

+        int result = 1;

+        result = prime * result + ((channel == null) ? 0 : channel.hashCode());

+        return result;

+    }

+

+    @Override

+    public boolean equals(Object obj) {

+        if (this == obj) return true;

+        if (obj == null) return false;

+        if (getClass() != obj.getClass()) return false;

+        HeaderExchangeChannel other = (HeaderExchangeChannel) obj;

+        if (channel == null) {

+            if (other.channel != null) return false;

+        } else if (!channel.equals(other.channel)) return false;

+        return true;

+    }

+

+    @Override

+    public String toString() {

+        return channel.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
new file mode 100644
index 0000000..102b22b
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
@@ -0,0 +1,200 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support.header;

+

+import java.net.InetSocketAddress;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+

+/**

+ * DefaultMessageClient

+ * 

+ * @author william.liangf

+ */

+public class HeaderExchangeClient implements ExchangeClient {

+

+    private static final Logger logger = LoggerFactory.getLogger( HeaderExchangeClient.class );

+

+    private final ScheduledExecutorService scheduled =

+            Executors.newScheduledThreadPool( 1,

+                                              new NamedThreadFactory(

+                                                      "dubbo-remoting-client-heartbeat",

+                                                      true ) );

+

+    // 心跳定时器

+    private ScheduledFuture<?> heatbeatTimer;

+

+    // 心跳超时,毫秒。缺省0,不会执行心跳。

+    private int heartbeat;

+

+    private int heartbeatTimeout;

+    

+    private final Client client;

+

+    private final ExchangeChannel channel;

+

+    public HeaderExchangeClient(Client client){

+        if (client == null) {

+            throw new IllegalArgumentException("client == null");

+        }

+        this.client = client;

+        this.channel = new HeaderExchangeChannel(client);

+        this.heartbeat = client.getUrl().getParameter( Constants.HEARTBEAT_KEY, Constants.DEFAULT_HEARTBEAT );

+        this.heartbeatTimeout = client.getUrl().getParameter( Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3 );

+        if ( heartbeatTimeout < heartbeat * 2 ) {

+            throw new IllegalStateException( "heartbeatTimeout < heartbeatInterval * 2" );

+        }

+        startHeatbeatTimer();

+    }

+

+    public ResponseFuture request(Object request) throws RemotingException {

+        return channel.request(request);

+    }

+

+    public URL getUrl() {

+        return channel.getUrl();

+    }

+

+    public InetSocketAddress getRemoteAddress() {

+        return channel.getRemoteAddress();

+    }

+

+    public ResponseFuture request(Object request, int timeout) throws RemotingException {

+        return channel.request(request, timeout);

+    }

+

+    public ChannelHandler getChannelHandler() {

+        return channel.getChannelHandler();

+    }

+

+    public boolean isConnected() {

+        return channel.isConnected();

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return channel.getLocalAddress();

+    }

+

+    public ExchangeHandler getExchangeHandler() {

+        return channel.getExchangeHandler();

+    }

+    

+    public void send(Object message) throws RemotingException {

+        channel.send(message);

+    }

+    

+    public void send(Object message, boolean sent) throws RemotingException {

+        channel.send(message, sent);

+    }

+

+    public boolean isClosed() {

+        return channel.isClosed();

+    }

+

+    public void close() {

+        doClose();

+        channel.close();

+    }

+

+    public void close(int timeout) {

+        doClose();

+        channel.close(timeout);

+    }

+

+    public void reset(URL url) {

+        client.reset(url);

+    }

+    

+    @Deprecated

+    public void reset(com.alibaba.dubbo.common.Parameters parameters){

+        reset(getUrl().addParameters(parameters.getParameters()));

+    }

+

+    public void reconnect() throws RemotingException {

+        client.reconnect();

+    }

+

+    public Object getAttribute(String key) {

+        return channel.getAttribute(key);

+    }

+

+    public void setAttribute(String key, Object value) {

+        channel.setAttribute(key, value);

+    }

+

+    public void removeAttribute(String key) {

+        channel.removeAttribute(key);

+    }

+

+    public boolean hasAttribute(String key) {

+        return channel.hasAttribute(key);

+    }

+

+    private void startHeatbeatTimer() {

+        stopHeartbeatTimer();

+        if ( heartbeat > 0 ) {

+            heatbeatTimer = scheduled.scheduleWithFixedDelay(

+                    new HeartBeatTask( new HeartBeatTask.ChannelProvider() {

+                        public Collection<Channel> getChannels() {

+                            return Collections.<Channel>singletonList( channel );

+                        }

+                    }, heartbeat, heartbeatTimeout ),

+                    heartbeat, heartbeat, TimeUnit.MILLISECONDS );

+        }

+    }

+

+    private void stopHeartbeatTimer() {

+        if (heatbeatTimer != null && ! heatbeatTimer.isCancelled()) {

+            try {

+                heatbeatTimer.cancel(true);

+            } catch ( Throwable e ) {

+                if (logger.isWarnEnabled()) {

+                    logger.warn(e.getMessage(), e);

+                }

+            }

+        }

+        heatbeatTimer =null;

+    }

+

+    private void doClose() {

+        stopHeartbeatTimer();

+        try {

+            scheduled.shutdown();

+        } catch (Throwable e) {

+            if (logger.isWarnEnabled()) {

+                logger.warn(e.getMessage(), e);

+            }

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java
new file mode 100644
index 0000000..6ab7167
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java
@@ -0,0 +1,233 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support.header;

+

+import java.net.InetSocketAddress;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.ExecutionException;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.exchange.Response;

+import com.alibaba.dubbo.remoting.exchange.support.DefaultFuture;

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerDelegate;

+

+/**

+ * ExchangeReceiver

+ * 

+ * @author william.liangf

+ * @author chao.liuc

+ */

+public class HeaderExchangeHandler implements ChannelHandlerDelegate {

+

+    protected static final Logger logger              = LoggerFactory.getLogger(HeaderExchangeHandler.class);

+

+    public static String          KEY_READ_TIMESTAMP  = "READ_TIMESTAMP";

+

+    public static String          KEY_WRITE_TIMESTAMP = "WRITE_TIMESTAMP";

+

+    private final ExchangeHandler handler;

+

+    public HeaderExchangeHandler(ExchangeHandler handler){

+        if (handler == null) {

+            throw new IllegalArgumentException("handler == null");

+        }

+        this.handler = handler;

+    }

+    

+    void handlerEvent(Channel channel, Request req) throws RemotingException{

+        if (req.getData() != null && req.getData().equals(Request.READONLY_EVENT)){

+            channel.setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);

+        }

+        if (req.isTwoWay()){

+            if (req.isHeartbeat()) {

+                Response res = new Response(req.getId(), req.getVersion());

+                res.setEvent(req.getData() == null ? null : req.getData().toString());

+                channel.send(res);

+            }

+        }

+    }

+

+    Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {

+        Response res = new Response(req.getId(), req.getVersion());

+        if (req.isBroken()) {

+            Object data = req.getData();

+

+            String msg;

+            if (data == null) msg = null;

+            else if (data instanceof Throwable) msg = StringUtils.toString((Throwable) data);

+            else msg = data.toString();

+            res.setErrorMessage("Fail to decode request due to: " + msg);

+            res.setStatus(Response.BAD_REQUEST);

+

+            return res;

+        }

+        // find handler by message class.

+        Object msg = req.getData();

+        try {

+            // handle data.

+            Object result = handler.reply(channel, msg);

+            res.setStatus(Response.OK);

+            res.setResult(result);

+        } catch (Throwable e) {

+            res.setStatus(Response.SERVICE_ERROR);

+            res.setErrorMessage(StringUtils.toString(e));

+        }

+        return res;

+    }

+

+    static void handleResponse(Channel channel, Response response) throws RemotingException {

+        if (response != null && !response.isHeartbeat()) {

+            DefaultFuture.received(channel, response);

+        }

+    }

+

+    public void connected(Channel channel) throws RemotingException {

+        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());

+        channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());

+        ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);

+        try {

+            handler.connected(exchangeChannel);

+        } finally {

+            HeaderExchangeChannel.removeChannelIfDisconnected(channel);

+        }

+    }

+

+    public void disconnected(Channel channel) throws RemotingException {

+        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());

+        channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());

+        ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);

+        try {

+            handler.disconnected(exchangeChannel);

+        } finally {

+            HeaderExchangeChannel.removeChannelIfDisconnected(channel);

+        }

+    }

+

+    public void sent(Channel channel, Object message) throws RemotingException {

+        Throwable exception = null;

+        try {

+            channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());

+            ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);

+            try {

+                handler.sent(exchangeChannel, message);

+            } finally {

+                HeaderExchangeChannel.removeChannelIfDisconnected(channel);

+            }

+        } catch (Throwable t) {

+            exception = t;

+        }

+        if (message instanceof Request) {

+            Request request = (Request) message;

+            DefaultFuture.sent(channel, request);

+        }

+        if (exception != null) {

+            if (exception instanceof RuntimeException) {

+                throw (RuntimeException) exception;

+            } else if (exception instanceof RemotingException) {

+                throw (RemotingException) exception;

+            } else {

+                throw new RemotingException(channel.getLocalAddress(), channel.getRemoteAddress(),

+                                            exception.getMessage(), exception);

+            }

+        }

+    }

+

+    private static boolean isClientSide(Channel channel) {

+        InetSocketAddress address = channel.getRemoteAddress();

+        URL url = channel.getUrl();

+        return url.getPort() == address.getPort() && 

+                    NetUtils.filterLocalHost(url.getIp())

+                    .equals(NetUtils.filterLocalHost(address.getAddress().getHostAddress()));

+    }

+

+    public void received(Channel channel, Object message) throws RemotingException {

+        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());

+        ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);

+        try {

+            if (message instanceof Request) {

+                // handle request.

+                Request request = (Request) message;

+                if (request.isEvent()){

+                    handlerEvent(channel, request);

+                } else {

+                    if (request.isTwoWay()) {

+                        Response response = handleRequest(exchangeChannel, request);

+                        channel.send(response);

+                    } else {

+                        handler.received(exchangeChannel, request.getData());

+                    }

+                }

+            } else if (message instanceof Response) {

+                handleResponse(channel, (Response) message);

+            } else if (message instanceof String) {

+                if (isClientSide(channel)) {

+                    Exception e = new Exception("Dubbo client can not supported string message: " + message + " in channel: " + channel + ", url: " + channel.getUrl());

+                    logger.error(e.getMessage(), e);

+                } else {

+                    String echo = handler.telnet(channel, (String) message);

+                    if (echo != null && echo.length() > 0) {

+                        channel.send(echo);

+                    }

+                }

+            } else {

+                handler.received(exchangeChannel, message);

+            }

+        } finally {

+            HeaderExchangeChannel.removeChannelIfDisconnected(channel);

+        }

+    }

+

+    public void caught(Channel channel, Throwable exception) throws RemotingException {

+        if (exception instanceof ExecutionException) {

+            ExecutionException e = (ExecutionException) exception;

+            Object msg = e.getRequest();

+            if (msg instanceof Request) {

+                Request req = (Request) msg;

+                if (req.isTwoWay() && ! req.isHeartbeat()) {

+                    Response res = new Response(req.getId(), req.getVersion());

+                    res.setStatus(Response.SERVER_ERROR);

+                    res.setErrorMessage(StringUtils.toString(e));

+                    channel.send(res);

+                    return;

+                }

+            }

+        }

+        ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);

+        try {

+            handler.caught(exchangeChannel, exception);

+        } finally {

+            HeaderExchangeChannel.removeChannelIfDisconnected(channel);

+        }

+    }

+

+    public ChannelHandler getHandler() {

+        if (handler instanceof ChannelHandlerDelegate) {

+            return ((ChannelHandlerDelegate) handler).getHandler();

+        } else {

+            return handler;

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java
new file mode 100644
index 0000000..091376c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java
@@ -0,0 +1,261 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support.header;

+

+import java.net.InetSocketAddress;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.exchange.support.DefaultFuture;

+

+/**

+ * ExchangeServerImpl

+ * 

+ * @author william.liangf

+ */

+public class HeaderExchangeServer implements ExchangeServer {

+    

+    protected final Logger        logger = LoggerFactory.getLogger(getClass());

+

+    private final ScheduledExecutorService scheduled                 = Executors.newScheduledThreadPool(1,

+                                                                                                        new NamedThreadFactory(

+                                                                                                                               "dubbo-remoting-server-heartbeat",

+                                                                                                                               true));

+

+    // 心跳定时器

+    private ScheduledFuture<?> heatbeatTimer;

+

+    // 心跳超时,毫秒。缺省0,不会执行心跳。

+    private int                            heartbeat;

+

+    private int                            heartbeatTimeout;

+    

+    private final Server server;

+

+    private volatile boolean closed = false;

+

+    public HeaderExchangeServer(Server server) {

+        if (server == null) {

+            throw new IllegalArgumentException("server == null");

+        }

+        this.server = server;

+        this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, Constants.DEFAULT_HEARTBEAT);

+        this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);

+        if (heartbeatTimeout < heartbeat * 2) {

+            throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");

+        }

+        startHeatbeatTimer();

+    }

+    

+    public Server getServer() {

+        return server;

+    }

+

+    public boolean isClosed() {

+        return server.isClosed();

+    }

+

+    private boolean isRunning() {

+        Collection<Channel> channels = getChannels();

+        for (Channel channel : channels) {

+            if (DefaultFuture.hasFuture(channel)) {

+                return true;

+            }

+        }

+        return false;

+    }

+

+    public void close() {

+        doClose();

+        server.close();

+    }

+

+    public void close(final int timeout) {

+        if (timeout > 0) {

+            final long max = (long) timeout;

+            final long start = System.currentTimeMillis();

+            if (getUrl().getParameter(Constants.CHANNEL_SEND_READONLYEVENT_KEY, false)){

+                sendChannelReadOnlyEvent();

+            }

+            while (HeaderExchangeServer.this.isRunning() 

+                    && System.currentTimeMillis() - start < max) {

+                try {

+                    Thread.sleep(10);

+                } catch (InterruptedException e) {

+                    logger.warn(e.getMessage(), e);

+                }

+            }

+        }

+        doClose();

+        server.close(timeout);

+    }

+    

+    private void sendChannelReadOnlyEvent(){

+        Request request = new Request();

+        request.setEvent(Request.READONLY_EVENT);

+        request.setTwoWay(false);

+        request.setVersion(Version.getVersion());

+        

+        Collection<Channel> channels = getChannels();

+        for (Channel channel : channels) {

+            try {

+                if (channel.isConnected())channel.send(request, getUrl().getParameter(Constants.CHANNEL_READONLYEVENT_SENT_KEY, true));

+            } catch (RemotingException e) {

+                logger.warn("send connot write messge error.", e);

+            }

+        }

+    }

+    

+    private void doClose() {

+        if (closed) {

+            return;

+        }

+        closed = true;

+        stopHeartbeatTimer();

+        try {

+            scheduled.shutdown();

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+    }

+

+    public Collection<ExchangeChannel> getExchangeChannels() {

+        Collection<ExchangeChannel> exchangeChannels  = new ArrayList<ExchangeChannel>();

+        Collection<Channel> channels = server.getChannels();

+        if (channels != null && channels.size() > 0) {

+            for (Channel channel : channels) {

+                exchangeChannels.add(HeaderExchangeChannel.getOrAddChannel(channel));

+            }

+        }

+        return exchangeChannels;

+    }

+

+    public ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress) {

+        Channel channel = server.getChannel(remoteAddress);

+        return HeaderExchangeChannel.getOrAddChannel(channel);

+    }

+

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public Collection<Channel> getChannels() {

+        return (Collection)getExchangeChannels();

+    }

+

+    public Channel getChannel(InetSocketAddress remoteAddress) {

+        return getExchangeChannel(remoteAddress);

+    }

+

+    public boolean isBound() {

+        return server.isBound();

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return server.getLocalAddress();

+    }

+

+    public URL getUrl() {

+        return server.getUrl();

+    }

+

+    public ChannelHandler getChannelHandler() {

+        return server.getChannelHandler();

+    }

+

+    public void reset(URL url) {

+        server.reset(url);

+        try {

+            if (url.hasParameter(Constants.HEARTBEAT_KEY)

+                    || url.hasParameter(Constants.HEARTBEAT_TIMEOUT_KEY)) {

+                int h = url.getParameter(Constants.HEARTBEAT_KEY, heartbeat);

+                int t = url.getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, h * 3);

+                if (t < h * 2) {

+                    throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");

+                }

+                if (h != heartbeat || t != heartbeatTimeout) {

+                    heartbeat = h;

+                    heartbeatTimeout = t;

+                    startHeatbeatTimer();

+                }

+            }

+        } catch (Throwable t) {

+            logger.error(t.getMessage(), t);

+        }

+    }

+    

+    @Deprecated

+    public void reset(com.alibaba.dubbo.common.Parameters parameters){

+        reset(getUrl().addParameters(parameters.getParameters()));

+    }

+

+    public void send(Object message) throws RemotingException {

+        if (closed) {

+            throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The server " + getLocalAddress() + " is closed!");

+        }

+        server.send(message);

+    }

+

+    public void send(Object message, boolean sent) throws RemotingException {

+        if (closed) {

+            throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The server " + getLocalAddress() + " is closed!");

+        }

+        server.send(message, sent);

+    }

+

+    private void startHeatbeatTimer() {

+        stopHeartbeatTimer();

+        if (heartbeat > 0) {

+            heatbeatTimer = scheduled.scheduleWithFixedDelay(

+                    new HeartBeatTask( new HeartBeatTask.ChannelProvider() {

+                        public Collection<Channel> getChannels() {

+                            return Collections.unmodifiableCollection(

+                                    HeaderExchangeServer.this.getChannels() );

+                        }

+                    }, heartbeat, heartbeatTimeout), 

+                    heartbeat, heartbeat,TimeUnit.MILLISECONDS);

+        }

+    }

+

+    private void stopHeartbeatTimer() {

+        try {

+            ScheduledFuture<?> timer = heatbeatTimer;

+            if (timer != null && ! timer.isCancelled()) {

+                timer.cancel(true);

+            }

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        } finally {

+            heatbeatTimer =null;

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.java
new file mode 100644
index 0000000..76badec
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.java
@@ -0,0 +1,43 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.exchange.support.header;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Transporters;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.remoting.exchange.Exchanger;

+

+/**

+ * DefaultMessenger

+ * 

+ * @author william.liangf

+ */

+public class HeaderExchanger implements Exchanger {

+    

+    public static final String NAME = "header";

+

+    public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {

+        return new HeaderExchangeClient(Transporters.connect(url, new HeaderExchangeHandler(handler)));

+    }

+

+    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {

+        return new HeaderExchangeServer(Transporters.bind(url, new HeaderExchangeHandler(handler)));

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTask.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTask.java
new file mode 100644
index 0000000..e9eea90
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTask.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *    
+ *   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.dubbo.remoting.exchange.support.header;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.exchange.Request;
+
+import java.util.Collection;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+final class HeartBeatTask implements Runnable {
+
+    private static final Logger logger = LoggerFactory.getLogger( HeartBeatTask.class );
+
+    private ChannelProvider channelProvider;
+
+    private int             heartbeat;
+
+    private int             heartbeatTimeout;
+
+    HeartBeatTask( ChannelProvider provider, int heartbeat, int heartbeatTimeout ) {
+        this.channelProvider = provider;
+        this.heartbeat = heartbeat;
+        this.heartbeatTimeout = heartbeatTimeout;
+    }
+
+    public void run() {
+        try {
+            long now = System.currentTimeMillis();
+            for ( Channel channel : channelProvider.getChannels() ) {
+                if (channel.isClosed()) {
+                    continue;
+                }
+                try {
+                    Long lastRead = ( Long ) channel.getAttribute(
+                            HeaderExchangeHandler.KEY_READ_TIMESTAMP );
+                    Long lastWrite = ( Long ) channel.getAttribute(
+                            HeaderExchangeHandler.KEY_WRITE_TIMESTAMP );
+                    if ( ( lastRead != null && now - lastRead > heartbeat )
+                            || ( lastWrite != null && now - lastWrite > heartbeat ) ) {
+                        Request req = new Request();
+                        req.setVersion( "2.0.0" );
+                        req.setTwoWay( true );
+                        req.setEvent( Request.HEARTBEAT_EVENT );
+                        channel.send( req );
+                        if ( logger.isDebugEnabled() ) {
+                            logger.debug( "Send heartbeat to remote channel "
+                                                  + channel.getRemoteAddress() + "." );
+                        }
+                    }
+                    if ( lastRead != null && now - lastRead > heartbeatTimeout ) {
+                        logger.warn( "Close remote channel " + channel.getRemoteAddress()
+                                             + ", because heartbeat read idle time out." );
+                        channel.close();
+                    }
+                } catch ( Throwable t ) {
+                    logger.warn( "Exception when heartbeat to remote channel " + channel.getRemoteAddress(), t );
+                }
+            }
+        } catch ( Throwable t ) {
+            logger.info( "Exception when heartbeat to remote channel(s): ", t );
+        }
+    }
+
+    interface ChannelProvider {
+        Collection<Channel> getChannels();
+    }
+
+}
+
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java
new file mode 100644
index 0000000..da73d39
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet;

+

+import com.alibaba.dubbo.common.extension.SPI;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * TelnetHandler

+ * 

+ * @author william.liangf

+ */

+@SPI

+public interface TelnetHandler {

+

+    /**

+     * telnet.

+     * 

+     * @param channel

+     * @param message

+     */

+    String telnet(Channel channel, String message) throws RemotingException;

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java
new file mode 100644
index 0000000..9642a5a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java
@@ -0,0 +1,307 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet.codec;
+
+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+import java.io.UnsupportedEncodingException;

+import java.net.InetSocketAddress;

+import java.nio.charset.Charset;

+import java.util.Arrays;

+import java.util.LinkedList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.transport.codec.TransportCodec;

+
+/**
+ * TelnetCodec
+ * 
+ * @author heyman
+ * @author william.liangf

+ * @author chao.liuc
+ */
+public class TelnetCodec extends TransportCodec {
+
+    private static final Logger  logger = LoggerFactory.getLogger(TelnetCodec.class);
+    
+    private static final String HISTORY_LIST_KEY = "telnet.history.list";
+
+    private static final String HISTORY_INDEX_KEY = "telnet.history.index";
+    
+    private static final byte[] UP = new byte[] {27, 91, 65};
+    
+    private static final byte[] DOWN = new byte[] {27, 91, 66};
+
+    private static final List<?> ENTER  = Arrays.asList(new Object[] { new byte[] { '\r', '\n' } /* Windows Enter */, new byte[] { '\n' } /* Linux Enter */ });
+
+    private static final List<?> EXIT   = Arrays.asList(new Object[] { new byte[] { 3 } /* Windows Ctrl+C */, new byte[] { -1, -12, -1, -3, 6 } /* Linux Ctrl+C */, new byte[] { -1, -19, -1, -3, 6 } /* Linux Pause */ });
+
+    public void encode(Channel channel, OutputStream output, Object message) throws IOException {
+        if (message instanceof String) {
+            if (isClientSide(channel)) {
+                message = message + "\r\n";
+            }
+            byte[] msgData = ((String) message).getBytes(getCharset(channel).name());
+            output.write(msgData);

+            output.flush();
+        } else {
+            super.encode(channel, output, message);
+        }
+    }
+    
+    public Object decode(Channel channel, InputStream is) throws IOException {
+        int readable = is.available();
+        byte[] message = new byte[readable];
+        is.read(message);
+        return decode(channel, is, readable, message);
+    }
+
+    @SuppressWarnings("unchecked")

+    protected Object decode(Channel channel, InputStream is, int readable, byte[] message) throws IOException {

+        if (isClientSide(channel)) {

+            return toString(message, getCharset(channel));

+        }

+        checkPayload(channel, readable);

+        if (message == null || message.length == 0) {

+            return NEED_MORE_INPUT;

+        }

+        

+        if (message[message.length - 1] == '\b') { // Windows backspace echo

+            try {

+                boolean doublechar = message.length >= 3 && message[message.length - 3] < 0; // double byte char

+                channel.send(new String(doublechar ? new byte[] {32, 32, 8, 8} : new byte[] {32, 8}, getCharset(channel).name()));

+            } catch (RemotingException e) {

+                throw new IOException(StringUtils.toString(e));

+            }

+            return NEED_MORE_INPUT;

+        }

+        

+        for (Object command : EXIT) {

+            if (isEquals(message, (byte[]) command)) {

+                if (logger.isInfoEnabled()) {

+                    logger.info(new Exception("Close channel " + channel + " on exit command: " + Arrays.toString((byte[])command)));

+                }

+                channel.close();

+                return null;

+            }

+        }

+        

+        boolean up = endsWith(message, UP);

+        boolean down = endsWith(message, DOWN);

+        if (up || down) {

+            LinkedList<String> history = (LinkedList<String>) channel.getAttribute(HISTORY_LIST_KEY);

+            if (history == null || history.size() == 0) {

+                return NEED_MORE_INPUT;

+            }

+            Integer index = (Integer) channel.getAttribute(HISTORY_INDEX_KEY);

+            Integer old = index;

+            if (index == null) {

+                index = history.size() - 1;

+            } else {

+                if (up) {

+                    index = index - 1;

+                    if (index < 0) {

+                        index = history.size() - 1;

+                    }

+                } else {

+                    index = index + 1;

+                    if (index > history.size() - 1) {

+                        index = 0;

+                    }

+                }

+            }

+            if (old == null || ! old.equals(index)) {

+                channel.setAttribute(HISTORY_INDEX_KEY, index);

+                String value = history.get(index);

+                if (old != null && old >= 0 && old < history.size()) {

+                    String ov = history.get(old);

+                    StringBuilder buf = new StringBuilder();

+                    for (int i = 0; i < ov.length(); i ++) {

+                        buf.append("\b");

+                    }

+                    for (int i = 0; i < ov.length(); i ++) {

+                        buf.append(" ");

+                    }

+                    for (int i = 0; i < ov.length(); i ++) {

+                        buf.append("\b");

+                    }

+                    value = buf.toString() + value;

+                }

+                try {

+                    channel.send(value);

+                } catch (RemotingException e) {

+                    throw new IOException(StringUtils.toString(e));

+                }

+            }

+            return NEED_MORE_INPUT;

+        }

+        for (Object command : EXIT) {

+            if (isEquals(message, (byte[]) command)) {

+                if (logger.isInfoEnabled()) {

+                    logger.info(new Exception("Close channel " + channel + " on exit command " + command));

+                }

+                channel.close();

+                return null;

+            }

+        }

+        byte[] enter = null;

+        for (Object command : ENTER) {

+            if (endsWith(message, (byte[]) command)) {

+                enter = (byte[]) command;

+                break;

+            }

+        }

+        if (enter == null) {

+            return NEED_MORE_INPUT;

+        }

+        LinkedList<String> history = (LinkedList<String>) channel.getAttribute(HISTORY_LIST_KEY);

+        Integer index = (Integer) channel.getAttribute(HISTORY_INDEX_KEY);

+        channel.removeAttribute(HISTORY_INDEX_KEY);

+        if (history != null && history.size() > 0 && index != null && index >= 0 && index < history.size()) {

+            String value = history.get(index);

+            if (value != null) {

+                byte[] b1 = value.getBytes();

+                if (message != null && message.length > 0) {

+                    byte[] b2 = new byte[b1.length + message.length];

+                    System.arraycopy(b1, 0, b2, 0, b1.length);

+                    System.arraycopy(message, 0, b2, b1.length, message.length);

+                    message = b2;

+                } else {

+                    message = b1;

+                }

+            }

+        }

+        String result = toString(message, getCharset(channel));

+        if (result != null && result.trim().length() > 0) {

+            if (history == null) {

+                history = new LinkedList<String>();

+                channel.setAttribute(HISTORY_LIST_KEY, history);

+            }

+            if (history.size() == 0) {

+                history.addLast(result);

+            } else if (! result.equals(history.getLast())) {

+                history.remove(result);

+                history.addLast(result);

+                if (history.size() > 10) {

+                    history.removeFirst();

+                }

+            }

+        }

+        return result;

+    }

+    

+    private static boolean isClientSide(Channel channel) {
+        InetSocketAddress address = channel.getRemoteAddress();
+        URL url = channel.getUrl();
+        return url.getPort() == address.getPort() && 
+                    NetUtils.filterLocalHost(url.getIp())
+                    .equals(NetUtils.filterLocalHost(address.getAddress().getHostAddress()));
+    }
+
+    private static Charset getCharset(Channel channel) {
+        if (channel != null) {
+            Object attribute = channel.getAttribute(Constants.CHARSET_KEY);
+            if (attribute instanceof String) {
+                try {
+                    return Charset.forName((String) attribute);
+                } catch (Throwable t) {
+                    logger.warn(t.getMessage(), t);
+                }
+            } else if (attribute instanceof Charset) {
+                return (Charset) attribute;
+            }
+            URL url = channel.getUrl();
+            if (url != null) {
+                String parameter = url.getParameter(Constants.CHARSET_KEY);
+                if (parameter != null && parameter.length() > 0) {
+                    try {
+                        return Charset.forName(parameter);
+                    } catch (Throwable t) {
+                        logger.warn(t.getMessage(), t);
+                    }
+                }
+            }
+        }
+        try {
+            return Charset.forName("GBK");
+        } catch (Throwable t) {
+            logger.warn(t.getMessage(), t);
+        }
+        return Charset.defaultCharset();
+    }
+
+    private static String toString(byte[] message, Charset charset) throws UnsupportedEncodingException {
+        byte[] copy = new byte[message.length];
+        int index = 0;
+        for (int i = 0; i < message.length; i ++) {
+            byte b = message[i] ;
+            if (b == '\b') { // backspace
+                if (index > 0) {
+                    index --;
+                }
+                if (i > 2 && message[i - 2] < 0) { // double byte char
+                    if (index > 0) {
+                        index --;
+                    }
+                }
+            } else if (b == 27) { // escape
+                if (i < message.length - 4 && message[i + 4] == 126) {
+                    i = i + 4;
+                } else if (i < message.length - 3 && message[i + 3] == 126) {
+                    i = i + 3;
+                } else if (i < message.length - 2) {
+                    i = i + 2;
+                }
+            } else if (b == -1 && i < message.length - 2 
+                    && (message[i + 1] == -3 || message[i + 1] == -5)) { // handshake
+                i = i + 2;
+            } else {
+                copy[index ++] = message[i];
+            }
+        }
+        if (index == 0) {
+            return "";
+        }
+        return new String(copy, 0, index, charset.name()).trim();
+    }
+
+    private static boolean isEquals(byte[] message, byte[] command) throws IOException {
+        return message.length == command.length && endsWith(message, command);
+    }
+
+    private static boolean endsWith(byte[] message, byte[] command) throws IOException {
+        if (message.length < command.length) {
+            return false;
+        }
+        int offset = message.length - command.length;
+        for (int i = command.length - 1; i >= 0 ; i --) {
+            if (message[offset + i] != command[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/Help.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/Help.java
new file mode 100644
index 0000000..8b6c1a9
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/Help.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet.support;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+/**

+ * Help

+ * 

+ * @author william.liangf

+ */

+@Documented

+@Retention(RetentionPolicy.RUNTIME)

+@Target({ElementType.TYPE})

+public @interface Help {

+

+    String parameter() default "";

+

+    String summary();

+

+    String detail() default "";

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.java
new file mode 100644
index 0000000..87be74c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.java
@@ -0,0 +1,78 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet.support;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;

+

+/**

+ * TelnetHandlerDispather

+ * 

+ * @author william.liangf

+ */

+public class TelnetHandlerAdapter extends ChannelHandlerAdapter implements TelnetHandler {

+

+    private final ExtensionLoader<TelnetHandler> extensionLoader = ExtensionLoader.getExtensionLoader(TelnetHandler.class);

+

+    public String telnet(Channel channel, String message) throws RemotingException {

+        String prompt = channel.getUrl().getParameterAndDecoded(Constants.PROMPT_KEY, Constants.DEFAULT_PROMPT);

+        boolean noprompt = message.contains("--no-prompt");

+        message = message.replace("--no-prompt", "");

+        StringBuilder buf = new StringBuilder();

+        message = message.trim();

+        String command;

+        if (message.length() > 0) {

+            int i = message.indexOf(' ');

+            if (i > 0) {

+                command = message.substring(0, i).trim();

+                message = message.substring(i + 1).trim();

+            } else {

+                command = message;

+                message = "";

+            }

+        } else {

+            command = "";

+        }

+        if (command.length() > 0) {

+            if (extensionLoader.hasExtension(command)) {

+                try {

+                    String result = extensionLoader.getExtension(command).telnet(channel, message);

+                    if (result == null) {

+                        return null;

+                    }

+                    buf.append(result);

+                } catch (Throwable t) {

+                    buf.append(t.getMessage());

+                }

+            } else {

+                buf.append("Unsupported command: ");

+                buf.append(command);

+            }

+        }

+        if (buf.length() > 0) {

+            buf.append("\r\n");

+        }

+        if (prompt != null && prompt.length() > 0 && ! noprompt) {

+            buf.append(prompt);

+        }

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetUtils.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetUtils.java
new file mode 100644
index 0000000..02aaa63
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetUtils.java
@@ -0,0 +1,160 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet.support;

+

+import java.util.Arrays;

+import java.util.List;

+

+/**

+ * TelnetUtils

+ * 

+ * @author william.liangf

+ */

+public class TelnetUtils {

+    

+    public static String toList(List<List<String>> table) {

+        int[] widths = new int[table.get(0).size()];

+        for (int j = 0; j < widths.length; j ++) {

+            for (List<String> row : table) {

+                widths[j] = Math.max(widths[j], row.get(j).length());

+            }

+        }

+        StringBuilder buf = new StringBuilder();

+        for (List<String> row : table) {

+            if (buf.length() > 0) {

+                buf.append("\r\n");

+            }

+            for (int j = 0; j < widths.length; j ++) {

+                if (j > 0) {

+                    buf.append(" - ");

+                }

+                String value = row.get(j);

+                buf.append(value);

+                if (j < widths.length - 1) {

+                    int pad = widths[j] - value.length();

+                    if (pad > 0) {

+                        for (int k = 0; k < pad; k ++) {

+                            buf.append(" ");

+                        }

+                    }

+                }

+            }

+        }

+        return buf.toString();

+    }

+    

+    public static String toTable(String[] header, List<List<String>> table) {

+        return toTable(Arrays.asList(header), table);

+    }

+    

+    public static String toTable(List<String> header, List<List<String>> table) {

+        int totalWidth = 0 ;

+        int[] widths = new int[header.size()];

+        int maxwidth = 70;

+        int maxcountbefore = 0;

+        for (int j = 0; j < widths.length; j ++) {

+            widths[j] = Math.max(widths[j], header.get(j).length());

+        }

+        for (List<String> row : table) {

+            int countbefore =  0;

+            for (int j = 0; j < widths.length; j ++) {

+                widths[j] = Math.max(widths[j], row.get(j).length() );

+                totalWidth = (totalWidth + widths[j])> maxwidth ? maxwidth : (totalWidth + widths[j]);

+                if (j<widths.length -1){

+                    countbefore = countbefore + widths[j];

+                }

+            }

+            maxcountbefore = Math.max(countbefore, maxcountbefore);

+        }

+        widths[widths.length-1] = Math.min(widths[widths.length-1], maxwidth-maxcountbefore);

+        StringBuilder buf = new StringBuilder();

+        //line

+        buf.append("+");

+        for (int j = 0; j < widths.length; j ++) {

+            for (int k = 0; k < widths[j] + 2; k ++) {

+                buf.append("-");

+            }

+            buf.append("+");

+        }

+        buf.append("\r\n");

+        //header

+        buf.append("|");

+        for (int j = 0; j < widths.length; j ++) {

+            String cell = header.get(j);

+            buf.append(" ");

+            buf.append(cell);

+            int pad = widths[j] - cell.length();

+            if (pad > 0) {

+                for (int k = 0; k < pad; k ++) {

+                    buf.append(" ");

+                }

+            }

+            buf.append(" |");

+        }

+        buf.append("\r\n");

+        //line

+        buf.append("+");

+        for (int j = 0; j < widths.length; j ++) {

+            for (int k = 0; k < widths[j] + 2; k ++) {

+                buf.append("-");

+            }

+            buf.append("+");

+        }

+        buf.append("\r\n");

+        //content

+        for (List<String> row : table) {

+            StringBuffer rowbuf = new StringBuffer();

+            rowbuf.append("|");

+            for (int j = 0; j < widths.length; j ++) {

+                String cell = row.get(j);

+                rowbuf.append(" ");

+                int remaing = cell.length();

+                while (remaing > 0){

+                    

+                    if (rowbuf.length() >= totalWidth ){

+                        buf.append(rowbuf.toString());

+                        rowbuf = new StringBuffer();

+//                        for(int m = 0;m < maxcountbefore && maxcountbefore < totalWidth ; m++){

+//                            rowbuf.append(" ");

+//                        }

+                    }

+                    

+                    rowbuf.append(cell.substring(cell.length()-remaing, cell.length()-remaing +1));

+                    remaing -- ;

+                }

+                int pad = widths[j] - cell.length();

+                if (pad > 0) {

+                    for (int k = 0; k < pad; k ++) {

+                        rowbuf.append(" ");

+                    }

+                } 

+                rowbuf.append(" |");

+            }

+            buf.append(rowbuf).append("\r\n");

+        }

+        //line

+        buf.append("+");

+        for (int j = 0; j < widths.length; j ++) {

+            for (int k = 0; k < widths[j] + 2; k ++) {

+                buf.append("-");

+            }

+            buf.append("+");

+        }

+        buf.append("\r\n");

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java
new file mode 100644
index 0000000..13772e4
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet.support.command;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+

+/**

+ * ClearTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "[lines]", summary = "Clear screen.", detail = "Clear screen.")

+public class ClearTelnetHandler implements TelnetHandler {

+

+    public String telnet(Channel channel, String message) {

+        int lines = 100;

+        if (message.length() > 0) {

+            if (! StringUtils.isInteger(message)) {

+                return "Illegal lines " + message + ", must be integer.";

+            }

+            lines = Integer.parseInt(message);

+        }

+        StringBuilder buf = new StringBuilder();

+        for (int i = 0; i < lines; i ++) {

+            buf.append("\r\n");

+        }

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java
new file mode 100644
index 0000000..7b4d328
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet.support.command;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+

+/**

+ * ExitTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "", summary = "Exit the telnet.", detail = "Exit the telnet.")

+public class ExitTelnetHandler implements TelnetHandler {

+

+    public String telnet(Channel channel, String message) {

+        channel.close();

+        return null;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java
new file mode 100644
index 0000000..d122792
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java
@@ -0,0 +1,72 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet.support.command;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+import com.alibaba.dubbo.remoting.telnet.support.TelnetUtils;

+

+/**

+ * HelpTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "[command]", summary = "Show help.", detail = "Show help.")

+public class HelpTelnetHandler implements TelnetHandler {

+    

+    private final ExtensionLoader<TelnetHandler> extensionLoader = ExtensionLoader.getExtensionLoader(TelnetHandler.class);

+

+    public String telnet(Channel channel, String message) {

+        if (message.length() > 0) {

+            if (! extensionLoader.hasExtension(message)) {

+                return "No such command " + message;

+            }

+            TelnetHandler handler = extensionLoader.getExtension(message);

+            Help help = handler.getClass().getAnnotation(Help.class);

+            StringBuilder buf = new StringBuilder();

+            buf.append("Command:\r\n    ");

+            buf.append(message + " " + help.parameter().replace("\r\n", " ").replace("\n", " "));

+            buf.append("\r\nSummary:\r\n    ");

+            buf.append(help.summary().replace("\r\n", " ").replace("\n", " "));

+            buf.append("\r\nDetail:\r\n    ");

+            buf.append(help.detail().replace("\r\n", "    \r\n").replace("\n", "    \n"));

+            return buf.toString();

+        } else {

+            List<List<String>> table = new ArrayList<List<String>>();

+            List<TelnetHandler> handlers = extensionLoader.getActivateExtension(channel.getUrl(), "telnet");

+            if (handlers != null && handlers.size() > 0) {

+                for (TelnetHandler handler : handlers) {

+                    Help help = handler.getClass().getAnnotation(Help.class);

+                    List<String> row = new ArrayList<String>();

+                    String parameter = " " + extensionLoader.getExtensionName(handler) + " " + (help != null ? help.parameter().replace("\r\n", " ").replace("\n", " ") : "");

+                    row.add(parameter.length() > 50 ? parameter.substring(0, 50) + "..." : parameter);

+                    String summary = help != null ? help.summary().replace("\r\n", " ").replace("\n", " ") : "";

+                    row.add(summary.length() > 50 ? summary.substring(0, 50) + "..." : summary);

+                    table.add(row);

+                }

+            }

+            return "Please input \"help [command]\" show detail.\r\n" + TelnetUtils.toList(table);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/LogTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/LogTelnetHandler.java
new file mode 100644
index 0000000..166ca0d
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/LogTelnetHandler.java
@@ -0,0 +1,94 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet.support.command;
+
+
+import java.io.File;

+import java.io.FileInputStream;

+import java.nio.ByteBuffer;

+import java.nio.channels.FileChannel;

+import java.text.SimpleDateFormat;

+import java.util.Date;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Level;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+
+/**
+ * LogTelnetHandler
+ * @author chao.liuc
+ * 
+ */

+@Activate
+@Help(parameter = "level", summary = "Change log level or show log ", detail = "Change log level or show log")
+public class LogTelnetHandler implements TelnetHandler {
+    
+    public static final String SERVICE_KEY = "telnet.log";
+
+    public String telnet(Channel channel, String message) {
+        long size = 0 ;
+        File file = LoggerFactory.getFile();
+        StringBuffer buf = new StringBuffer();
+        if (message == null || message.trim().length() == 0) {
+            buf.append("EXAMPLE: log error / log 100");
+        }else {
+            String str[] = message.split(" ");
+            if (! StringUtils.isInteger(str[0])){
+                LoggerFactory.setLevel(Level.valueOf(message.toUpperCase()));
+            } else {
+                int SHOW_LOG_LENGTH = Integer.parseInt(str[0]);
+                
+                if (file != null && file.exists()) {
+                    try{
+                        FileInputStream fis = new FileInputStream(file);
+                        FileChannel filechannel = fis.getChannel();
+                        size = filechannel.size();
+                        ByteBuffer bb;
+                        if (size <= SHOW_LOG_LENGTH) {
+                            bb = ByteBuffer.allocate((int) size);
+                            filechannel.read(bb, 0);
+                        } else {
+                            int pos = (int) (size - SHOW_LOG_LENGTH);
+                            bb = ByteBuffer.allocate(SHOW_LOG_LENGTH);
+                            filechannel.read(bb, pos);
+                        }
+                        bb.flip();
+                        String content = new String(bb.array()).replace("<", "&lt;")
+                        .replace(">", "&gt;").replace("\n", "<br/><br/>");
+                        buf.append("\r\ncontent:"+content);
+                        
+                        buf.append("\r\nmodified:"+(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+                        .format(new Date(file.lastModified()))));
+                        buf.append("\r\nsize:"+size +"\r\n");
+                    }catch (Exception e) {
+                        buf.append(e.getMessage());
+                    }
+                }else {
+                    size = 0;
+                    buf.append("\r\nMESSAGE: log file not exists or log appender is console .");
+                }
+            }
+        }
+        buf.append("\r\nCURRENT LOG LEVEL:"+ LoggerFactory.getLevel())
+        .append("\r\nCURRENT LOG APPENDER:"+ (file == null ? "console" : file.getAbsolutePath()));
+        return buf.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
new file mode 100644
index 0000000..7014a6c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
@@ -0,0 +1,99 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.telnet.support.command;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.common.status.support.StatusUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+import com.alibaba.dubbo.remoting.telnet.support.TelnetUtils;

+

+/**

+ * StatusTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "[-l]", summary = "Show status.", detail = "Show status.")

+public class StatusTelnetHandler implements TelnetHandler {

+

+    private final ExtensionLoader<StatusChecker> extensionLoader = ExtensionLoader.getExtensionLoader(StatusChecker.class);

+

+    public String telnet(Channel channel, String message) {

+        if (message.equals("-l")) {

+            List<StatusChecker> checkers = extensionLoader.getActivateExtension(channel.getUrl(), "status");

+            String[] header = new String[] {"resource", "status", "message"};

+            List<List<String>> table = new ArrayList<List<String>>();

+            Map<String, Status> statuses = new HashMap<String, Status>();

+            if (checkers != null && checkers.size() > 0) {

+                for (StatusChecker checker : checkers) {

+                    String name = extensionLoader.getExtensionName(checker);

+                    Status stat;

+                    try {

+                        stat = checker.check();

+                    } catch (Throwable t) {

+                        stat = new Status(Status.Level.ERROR, t.getMessage());

+                    }

+                    statuses.put(name, stat);

+                    if (stat.getLevel() != null && stat.getLevel() != Status.Level.UNKNOWN) {

+                        List<String> row = new ArrayList<String>();

+                        row.add(name);

+                        row.add(String.valueOf(stat.getLevel()));

+                        row.add(stat.getMessage() == null ? "" : stat.getMessage());

+                        table.add(row);

+                    }

+                }

+            }

+            Status stat= StatusUtils.getSummaryStatus(statuses);

+            List<String> row = new ArrayList<String>();

+            row.add("summary");

+            row.add(String.valueOf(stat.getLevel()));

+            row.add(stat.getMessage());

+            table.add(row);

+            return TelnetUtils.toTable(header, table);

+        } else if (message.length() > 0) {

+            return "Unsupported parameter " + message + " for status.";

+        }

+        String status = channel.getUrl().getParameter("status");

+        Map<String, Status> statuses = new HashMap<String, Status>();

+        if (status != null && status.length() > 0) {

+            String[] ss = Constants.COMMA_SPLIT_PATTERN.split(status);

+            for (String s : ss) {

+                StatusChecker handler = extensionLoader.getExtension(s);

+                Status stat;

+                try {

+                    stat = handler.check();

+                } catch (Throwable t) {

+                    stat = new Status(Status.Level.ERROR, t.getMessage());

+                }

+                statuses.put(s, stat);

+            }

+        }

+        Status stat= StatusUtils.getSummaryStatus(statuses);

+        return String.valueOf(stat.getLevel());

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractChannel.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractChannel.java
new file mode 100644
index 0000000..0bd04ba
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractChannel.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * AbstractChannel

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractChannel extends AbstractPeer implements Channel {

+

+    public AbstractChannel(URL url, ChannelHandler handler){

+        super(url, handler);

+    }

+

+    public void send(Object message, boolean sent) throws RemotingException {

+        if (isClosed()) {

+            throw new RemotingException(this, "Failed to send message "

+                                              + (message == null ? "" : message.getClass().getName()) + ":" + message

+                                              + ", cause: Channel closed. channel: " + getLocalAddress() + " -> " + getRemoteAddress());

+        }

+    }

+

+    @Override

+    public String toString() {

+        return getLocalAddress() + " -> " + getRemoteAddress();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractClient.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractClient.java
new file mode 100644
index 0000000..5ae5d9d
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractClient.java
@@ -0,0 +1,384 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;
+
+import java.net.InetSocketAddress;

+import java.util.concurrent.ExecutorService;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.ScheduledThreadPoolExecutor;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.atomic.AtomicBoolean;

+import java.util.concurrent.atomic.AtomicInteger;

+import java.util.concurrent.locks.Lock;

+import java.util.concurrent.locks.ReentrantLock;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ExecutorUtil;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelHandlers;

+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;

+
+/**
+ * AbstractClient
+ * 
+ * @author qian.lei
+ * @author chao.liuc
+ */
+public abstract class AbstractClient extends AbstractEndpoint implements Client {

+    

+    private static final Logger logger = LoggerFactory.getLogger(AbstractClient.class);
+    
+    protected static final String CLIENT_THREAD_POOL_NAME  ="DubboClientHandler";
+    
+    private static final AtomicInteger CLIENT_THREAD_POOL_ID = new AtomicInteger();
+
+    private final Lock            connectLock = new ReentrantLock();
+    
+    private static final ScheduledThreadPoolExecutor reconnectExecutorService = new ScheduledThreadPoolExecutor(2, new NamedThreadFactory("DubboClientReconnectTimer", true));
+    
+    private volatile  ScheduledFuture<?> reconnectExecutorFuture = null;
+    
+    protected volatile ExecutorService executor;

+    

+    private final boolean send_reconnect ;

+    

+    private final AtomicInteger reconnect_count = new AtomicInteger(0);

+    

+    //重连的error日志是否已经被调用过.

+    private final AtomicBoolean reconnect_error_log_flag = new AtomicBoolean(false) ;

+    

+    //重连warning的间隔.(waring多少次之后,warning一次) //for test

+    private final int reconnect_warning_period ;

+    

+    //the last successed connected time

+    private long lastConnectedTime = System.currentTimeMillis();

+    

+    private final long shutdown_timeout ;
+    
+    
+    public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
+        super(url, handler);

+        

+        send_reconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);

+        

+        shutdown_timeout = url.getParameter(Constants.SHUTDOWN_TIMEOUT_KEY, Constants.DEFAULT_SHUTDOWN_TIMEOUT);

+        

+        //默认重连间隔2s,1800表示1小时warning一次.

+        reconnect_warning_period = url.getParameter("reconnect.waring.period", 1800);

+        
+        try {
+            doOpen();
+        } catch (Throwable t) {
+            close();
+            throw new RemotingException(url.toInetSocketAddress(), null, 

+                                        "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() 
+                                        + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
+        }
+        try {
+            // connect.
+            connect();
+            if (logger.isInfoEnabled()) {
+                logger.info("Start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress());
+            }
+        } catch (RemotingException t) {
+            if (url.getParameter(Constants.CHECK_KEY, true)) {

+                close();
+                throw t;
+            } else {
+                logger.error("Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+                             + " connect to the server " + getRemoteAddress() + " (check == false, ignore and retry later!), cause: " + t.getMessage(), t);
+            }
+        } catch (Throwable t){
+            close();
+            throw new RemotingException(url.toInetSocketAddress(), null, 

+                    "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() 
+                    + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
+        }
+        
+        if (handler instanceof WrappedChannelHandler ){
+            executor = ((WrappedChannelHandler)handler).getExecutor();
+        }
+    }

+    

+    protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler){

+        url = url.addParameterIfAbsent(Constants.THREAD_NAME_KEY, CLIENT_THREAD_POOL_NAME)

+            .addParameterIfAbsent(Constants.THREADPOOL_KEY, Constants.DEFAULT_CLIENT_THREADPOOL);

+        return ChannelHandlers.wrap(handler, url);

+    }
+    
+    /**
+     * init reconnect thread
+     */
+    private synchronized void initConnectStatusCheckCommand(){
+        //reconnect=false to close reconnect 
+        int reconnect = getReconnectParam(getUrl());
+        if(reconnect > 0 && reconnectExecutorFuture == null){
+            Runnable connectStatusCheckCommand =  new Runnable() {

+                public void run() {
+                    try {
+                        if (! isConnected()) {
+                            connect();
+                        } else {

+                            lastConnectedTime = System.currentTimeMillis();

+                        }
+                    } catch (Throwable t) { 

+                        String errorMsg = "client reconnect to "+getUrl().getAddress()+" find error . url: "+ getUrl();

+                        int count = reconnect_count.incrementAndGet();

+                        // wait registry sync provider list

+                        if (System.currentTimeMillis() - lastConnectedTime > shutdown_timeout){

+                            if (!reconnect_error_log_flag.get()){

+                                reconnect_error_log_flag.set(true);

+                                logger.error(errorMsg, t);

+                                return ;

+                            }

+                        }

+                        if ( count % reconnect_warning_period == 0){

+                            logger.warn(errorMsg, t);

+                        }

+                    }
+                }
+            };
+            reconnectExecutorFuture = reconnectExecutorService.scheduleWithFixedDelay(connectStatusCheckCommand, reconnect, reconnect, TimeUnit.MILLISECONDS);
+        }
+    }

+    

+    /**

+     * @param url

+     * @return 0-false

+     */

+    private static int getReconnectParam(URL url){

+        int reconnect ;

+        String param = url.getParameter(Constants.RECONNECT_KEY);

+        if (param == null || param.length()==0 || "true".equalsIgnoreCase(param)){

+            reconnect = Constants.DEFAULT_RECONNECT_PERIOD;

+        }else if ("false".equalsIgnoreCase(param)){

+            reconnect = 0;

+        } else {

+            try{

+                reconnect = Integer.parseInt(param);

+            }catch (Exception e) {

+                throw new IllegalArgumentException("reconnect param must be nonnegative integer or false/true. input is:"+param);

+            }

+            if(reconnect < 0){

+                throw new IllegalArgumentException("reconnect param must be nonnegative integer or false/true. input is:"+param);

+            }

+        }

+        return reconnect;

+    }
+    
+    private synchronized void destroyConnectStatusCheckCommand(){
+        try {
+            if (reconnectExecutorFuture != null && ! reconnectExecutorFuture.isDone()){
+                reconnectExecutorFuture.cancel(true);
+                reconnectExecutorService.purge();
+            }
+        } catch (Throwable e) {
+            logger.warn(e.getMessage(), e);
+        }
+    }
+    
+    protected ExecutorService createExecutor() {
+        return Executors.newCachedThreadPool(new NamedThreadFactory(CLIENT_THREAD_POOL_NAME + CLIENT_THREAD_POOL_ID.incrementAndGet() + "-" + getUrl().getAddress(), true));
+    }
+    
+    public InetSocketAddress getConnectAddress() {
+        return new InetSocketAddress(NetUtils.filterLocalHost(getUrl().getHost()), getUrl().getPort());
+    }
+
+    public InetSocketAddress getRemoteAddress() {
+        Channel channel = getChannel();
+        if (channel == null)
+            return getUrl().toInetSocketAddress();

+        return channel.getRemoteAddress();
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        Channel channel = getChannel();
+        if (channel == null)
+            return InetSocketAddress.createUnresolved(NetUtils.getLocalHost(), 0);
+        return channel.getLocalAddress();
+    }
+
+    public boolean isConnected() {
+        Channel channel = getChannel();
+        if (channel == null)
+            return false;
+        return channel.isConnected();
+    }
+
+    public Object getAttribute(String key) {
+        Channel channel = getChannel();
+        if (channel == null)
+            return null;
+        return channel.getAttribute(key);
+    }
+
+    public void setAttribute(String key, Object value) {
+        Channel channel = getChannel();
+        if (channel == null)
+            return;
+        channel.setAttribute(key, value);
+    }
+
+    public void removeAttribute(String key) {
+        Channel channel = getChannel();
+        if (channel == null)
+            return;
+        channel.removeAttribute(key);
+    }
+
+    public boolean hasAttribute(String key) {
+        Channel channel = getChannel();
+        if (channel == null)
+            return false;
+        return channel.hasAttribute(key);
+    }
+    
+    public void send(Object message, boolean sent) throws RemotingException {

+        if (send_reconnect && !isConnected()){

+            connect();

+        }

+        Channel channel = getChannel();

+        //TODO getChannel返回的状态是否包含null需要改进
+        if (channel == null || ! channel.isConnected()) {
+          throw new RemotingException(this, "message can not send, because channel is closed . url:" + getUrl());
+        }
+        channel.send(message, sent);
+    }
+    
+    private void connect() throws RemotingException {
+        connectLock.lock();
+        try {
+            if (isConnected()) {
+                return;
+            }
+            initConnectStatusCheckCommand();
+            doConnect();
+            if (! isConnected()) {
+                throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+                                            + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+                                            + ", cause: Connect wait timeout: " + getTimeout() + "ms.");
+            }

+            reconnect_count.set(0);

+            reconnect_error_log_flag.set(false);
+        } catch (RemotingException e) {
+            throw e;
+        } catch (Throwable e) {
+            throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+                                        + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+                                        + ", cause: " + e.getMessage(), e);
+        } finally {

+            connectLock.unlock();
+        }

+    }
+
+    public void disconnect() {
+        connectLock.lock();
+        try {
+            destroyConnectStatusCheckCommand();
+            try {
+                Channel channel = getChannel();
+                if (channel != null) {
+                    channel.close();
+                }
+            } catch (Throwable e) {
+                logger.warn(e.getMessage(), e);
+            }
+            try {
+                doDisConnect();
+            } catch (Throwable e) {
+                logger.warn(e.getMessage(), e);
+            }
+        } finally {
+            connectLock.unlock();
+        }
+    }
+    
+    public void reconnect() throws RemotingException {
+        disconnect();
+        connect();
+    }
+    public void close() {
+        ExecutorUtil.shutdownNow(executor, 100);
+        try {
+            super.close();
+        } catch (Throwable e) {
+            logger.warn(e.getMessage(), e);
+        }
+        disconnect();
+        try {
+            doClose();
+        } catch (Throwable e) {
+            logger.warn(e.getMessage(), e);
+        }
+    }
+
+    public void close(int timeout) {
+        ExecutorUtil.gracefulShutdown(executor ,timeout);
+        close();
+    }
+    
+    @Override
+    public String toString() {
+        return getClass().getName() + " [" + getLocalAddress() + " -> " + getRemoteAddress() + "]";
+    }
+
+    /**
+     * Open client.
+     * 
+     * @throws Throwable
+     */
+    protected abstract void doOpen() throws Throwable;
+
+    /**
+     * Close client.
+     * 
+     * @throws Throwable
+     */
+    protected abstract void doClose() throws Throwable;
+
+    /**
+     * Connect to server.
+     * 
+     * @throws Throwable
+     */
+    protected abstract void doConnect() throws Throwable;
+    
+    /**
+     * disConnect to server.
+     * 
+     * @throws Throwable
+     */
+    protected abstract void doDisConnect() throws Throwable;
+
+    /**
+     * Get the connected channel.
+     * 
+     * @return channel
+     */
+    protected abstract Channel getChannel();
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractCodec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractCodec.java
new file mode 100644
index 0000000..83e5cd9
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractCodec.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;

+

+import java.io.IOException;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.serialize.Serialization;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.Codec;

+

+/**

+ * AbstractCodec

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractCodec implements Codec {

+

+    protected Serialization getSerialization(Channel channel) {

+        Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(channel.getUrl().getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));

+        return serialization;

+    }

+

+    protected void checkPayload(Channel channel, long size) throws IOException {

+        int payload = Constants.DEFAULT_PAYLOAD;

+        if (channel != null && channel.getUrl() != null) {

+            payload = channel.getUrl().getPositiveParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD);

+        }

+        if (size > payload) {

+            throw new IOException("Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractEndpoint.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractEndpoint.java
new file mode 100644
index 0000000..7c9fd91
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractEndpoint.java
@@ -0,0 +1,101 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;
+
+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.Resetable;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Codec;

+
+/**
+ * AbstractEndpoint
+ * 
+ * @author william.liangf
+ */
+public abstract class AbstractEndpoint extends AbstractPeer implements Resetable {

+    

+    private static final Logger logger = LoggerFactory.getLogger(AbstractEndpoint.class);
+
+    private Codec                 codec;
+
+    private int                   timeout;
+
+    private int                   connectTimeout;
+    
+    public AbstractEndpoint(URL url, ChannelHandler handler) {
+        super(url, handler);
+        this.codec = ExtensionLoader.getExtensionLoader(Codec.class).getExtension(url.getParameter(Constants.CODEC_KEY, "telnet"));
+        this.timeout = url.getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+        this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, timeout);
+    }
+
+    public void reset(URL url) {
+        if (isClosed()) {
+            throw new IllegalStateException("Failed to reset parameters "
+                                        + url + ", cause: Channel closed. channel: " + getLocalAddress());
+        }
+        try {
+            if (url.hasParameter(Constants.HEARTBEAT_KEY)) {
+                int t = url.getParameter(Constants.TIMEOUT_KEY, 0);
+                if (t > 0) {
+                    this.timeout = t;
+                }
+            }
+        } catch (Throwable t) {
+            logger.error(t.getMessage(), t);
+        }
+        try {
+            if (url.hasParameter(Constants.CONNECT_TIMEOUT_KEY)) {
+                int t = url.getParameter(Constants.CONNECT_TIMEOUT_KEY, 0);
+                if (t > 0) {
+                    this.connectTimeout = t;
+                }
+            }
+        } catch (Throwable t) {
+            logger.error(t.getMessage(), t);
+        }
+        try {
+            if (url.hasParameter(Constants.CODEC_KEY)) {
+                String c = url.getParameter(Constants.CODEC_KEY);
+                this.codec = ExtensionLoader.getExtensionLoader(Codec.class).getExtension(c);
+            }
+        } catch (Throwable t) {
+            logger.error(t.getMessage(), t);
+        }
+    }

+    

+    @Deprecated

+    public void reset(com.alibaba.dubbo.common.Parameters parameters){

+        reset(getUrl().addParameters(parameters.getParameters()));

+    }
+
+    protected Codec getCodec() {
+        return codec;
+    }
+
+    protected int getTimeout() {
+        return timeout;
+    }
+
+    protected int getConnectTimeout() {
+        return connectTimeout;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractPeer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractPeer.java
new file mode 100644
index 0000000..353d54a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractPeer.java
@@ -0,0 +1,129 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Endpoint;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * AbstractPeer

+ * 

+ * @author qian.lei

+ * @author william.liangf

+ */

+public abstract class AbstractPeer implements Endpoint, ChannelHandler {

+

+    private final ChannelHandler handler;

+

+    private volatile URL         url;

+

+    private volatile boolean     closed;

+

+    public AbstractPeer(URL url, ChannelHandler handler) {

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        if (handler == null) {

+            throw new IllegalArgumentException("handler == null");

+        }

+        this.url = url;

+        this.handler = handler;

+    }

+

+    public void send(Object message) throws RemotingException {

+        send(message, url.getParameter(Constants.SENT_KEY, false));

+    }

+

+    public void close() {

+        closed = true;

+    }

+

+    public void close(int timeout) {

+        close();

+    }

+

+    public URL getUrl() {

+        return url;

+    }

+

+    protected void setUrl(URL url) {

+        if (url == null) {

+            throw new IllegalArgumentException("url == null");

+        }

+        this.url = url;

+    }

+

+    public ChannelHandler getChannelHandler() {

+        if (handler instanceof ChannelHandlerDelegate) {

+            return ((ChannelHandlerDelegate) handler).getHandler();

+        } else {

+            return handler;

+        }

+    }

+    

+    /**

+     * @return ChannelHandler

+     */

+    @Deprecated

+    public ChannelHandler getHandler() {

+        return getDelegateHandler();

+    }

+    

+    /**

+     * 返回最终的handler,可能已被wrap,需要区别于getChannelHandler

+     * @return ChannelHandler

+     */

+    public ChannelHandler getDelegateHandler() {

+        return handler;

+    }

+    

+    public boolean isClosed() {

+        return closed;

+    }

+

+    public void connected(Channel ch) throws RemotingException {

+        if (closed) {

+            return;

+        }

+        handler.connected(ch);

+    }

+

+    public void disconnected(Channel ch) throws RemotingException {

+        handler.disconnected(ch);

+    }

+

+    public void sent(Channel ch, Object msg) throws RemotingException {

+        if (closed) {

+            return;

+        }

+        handler.sent(ch, msg);

+    }

+

+    public void received(Channel ch, Object msg) throws RemotingException {

+        if (closed) {

+            return;

+        }

+        handler.received(ch, msg);

+    }

+

+    public void caught(Channel ch, Throwable ex) throws RemotingException {

+        handler.caught(ch, ex);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.java
new file mode 100644
index 0000000..1b9a2c4
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.java
@@ -0,0 +1,213 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;
+
+import java.net.InetSocketAddress;

+import java.util.Collection;

+import java.util.concurrent.ExecutorService;

+import java.util.concurrent.ThreadPoolExecutor;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ExecutorUtil;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Codec;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;

+
+/**
+ * AbstractServer
+ * 
+ * @author qian.lei
+ * @author ding.lid
+ */
+public abstract class AbstractServer extends AbstractEndpoint implements Server {

+    

+    private static final Logger logger = LoggerFactory.getLogger(AbstractServer.class);

+
+    private InetSocketAddress              localAddress;
+
+    private InetSocketAddress              bindAddress;
+
+    private int                            accepts;
+
+    private int                            idleTimeout = 600; //600 seconds
+    
+    protected static final String SERVER_THREAD_POOL_NAME  ="DubboServerHandler";
+    
+    ExecutorService executor;
+
+    public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
+        super(url, handler);
+        localAddress = getUrl().toInetSocketAddress();

+        String host = url.getParameter(Constants.ANYHOST_KEY, false) 

+                        || NetUtils.isInvalidLocalHost(getUrl().getHost()) 
+                        ? NetUtils.ANYHOST : getUrl().getHost();
+        bindAddress = new InetSocketAddress(host, getUrl().getPort());
+        this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);

+        this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);

+        try {
+            doOpen();
+            if (logger.isInfoEnabled()) {
+                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
+            }
+        } catch (Throwable t) {
+            throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName() 

+                                        + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
+        }
+        if (handler instanceof WrappedChannelHandler ){
+            executor = ((WrappedChannelHandler)handler).getExecutor();
+        }
+    }
+    
+    protected abstract void doOpen() throws Throwable;
+    
+    protected abstract void doClose() throws Throwable;
+
+    public void reset(URL url) {
+        if (url == null) {
+            return;
+        }
+        try {
+            if (url.hasParameter(Constants.ACCEPTS_KEY)) {
+                int a = url.getParameter(Constants.ACCEPTS_KEY, 0);

+                if (a > 0) {
+                    this.accepts = a;
+                }
+            }
+        } catch (Throwable t) {
+            logger.error(t.getMessage(), t);
+        }
+        try {
+            if (url.hasParameter(Constants.IDLE_TIMEOUT_KEY)) {
+                int t = url.getParameter(Constants.IDLE_TIMEOUT_KEY, 0);

+                if (t > 0) {
+                    this.idleTimeout = t;
+                }
+            }
+        } catch (Throwable t) {
+            logger.error(t.getMessage(), t);
+        }
+        try {
+            if (url.hasParameter(Constants.THREADS_KEY) 
+                    && executor instanceof ThreadPoolExecutor && !executor.isShutdown()) {
+                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
+                int threads = url.getParameter(Constants.THREADS_KEY, 0);

+                int max = threadPoolExecutor.getMaximumPoolSize();
+                int core = threadPoolExecutor.getCorePoolSize();
+                if (threads > 0 && (threads != max || threads != core)) {
+                    if (threads < core) {
+                        threadPoolExecutor.setCorePoolSize(threads);
+                        if (core == max) {
+                            threadPoolExecutor.setMaximumPoolSize(threads);
+                        }
+                    } else {
+                        threadPoolExecutor.setMaximumPoolSize(threads);
+                        if (core == max) {
+                            threadPoolExecutor.setCorePoolSize(threads);
+                        }
+                    }
+                }
+            }
+        } catch (Throwable t) {
+            logger.error(t.getMessage(), t);
+        }
+        super.setUrl(getUrl().addParameters(url.getParameters()));
+    }
+
+    public void send(Object message, boolean sent) throws RemotingException {
+        Collection<Channel> channels = getChannels();
+        for (Channel channel : channels) {
+            if (channel.isConnected()) {

+                channel.send(message, sent);

+            }
+        }
+    }

+    

+    public void close() {

+        if (logger.isInfoEnabled()) {

+            logger.info("Close " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());

+        }

+        ExecutorUtil.shutdownNow(executor ,100);
+        try {
+            super.close();
+        } catch (Throwable e) {
+            logger.warn(e.getMessage(), e);
+        }
+        try {
+            doClose();
+        } catch (Throwable e) {
+            logger.warn(e.getMessage(), e);
+        }
+    }
+    
+    public void close(int timeout) {
+        ExecutorUtil.gracefulShutdown(executor ,timeout);
+        close();
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        return localAddress;
+    }
+    
+    public InetSocketAddress getBindAddress() {
+        return bindAddress;
+    }
+
+    public int getAccepts() {
+        return accepts;
+    }
+
+    public int getIdleTimeout() {
+        return idleTimeout;
+    }
+
+    @Override
+    public void connected(Channel ch) throws RemotingException {
+        Collection<Channel> channels = getChannels();
+        if (accepts > 0 && channels.size() > accepts) {
+            logger.error("Close channel " + ch + ", cause: The server " + ch.getLocalAddress() + " connections greater than max config " + accepts);
+            ch.close();
+            return;
+        }
+        super.connected(ch);
+    }
+    
+    @Override
+    public void disconnected(Channel ch) throws RemotingException {
+        Collection<Channel> channels = getChannels();
+        if (channels.size() == 0){
+            logger.warn("All clients has discontected from " + ch.getLocalAddress() + ". You can graceful shutdown now.");
+        }
+        super.disconnected(ch);
+    }
+    
+    protected Codec getDownstreamCodec() {
+        Codec downstreamCodec = getCodec();
+        String downstreamCodecStr = getUrl().getParameter(Constants.DOWNSTREAM_CODEC_KEY);
+        if(downstreamCodecStr != null ){
+            downstreamCodec = ExtensionLoader.getExtensionLoader(Codec.class).getExtension(downstreamCodecStr);
+        }
+        return downstreamCodec;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelDelegate.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelDelegate.java
new file mode 100644
index 0000000..639f95f
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelDelegate.java
@@ -0,0 +1,107 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;

+

+import java.net.InetSocketAddress;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * ChannelDelegate

+ * 

+ * @author william.liangf

+ */

+public class ChannelDelegate implements Channel {

+    

+    private transient Channel channel;

+    

+    public ChannelDelegate() {

+    }

+

+    public ChannelDelegate(Channel channel) {

+        setChannel(channel);

+    }

+    

+    public Channel getChannel() {

+        return channel;

+    }

+

+    public void setChannel(Channel channel) {

+        if (channel == null) {

+            throw new IllegalArgumentException("channel == null");

+        }

+        this.channel = channel;

+    }

+

+    public URL getUrl() {

+        return channel.getUrl();

+    }

+

+    public InetSocketAddress getRemoteAddress() {

+        return channel.getRemoteAddress();

+    }

+

+    public ChannelHandler getChannelHandler() {

+        return channel.getChannelHandler();

+    }

+

+    public boolean isConnected() {

+        return channel.isConnected();

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return channel.getLocalAddress();

+    }

+

+    public boolean hasAttribute(String key) {

+        return channel.hasAttribute(key);

+    }

+

+    public void send(Object message) throws RemotingException {

+        channel.send(message);

+    }

+

+    public Object getAttribute(String key) {

+        return channel.getAttribute(key);

+    }

+

+    public void setAttribute(String key, Object value) {

+        channel.setAttribute(key, value);

+    }

+

+    public void send(Object message, boolean sent) throws RemotingException {

+        channel.send(message, sent);

+    }

+

+    public void removeAttribute(String key) {

+        channel.removeAttribute(key);

+    }

+

+    public void close() {

+        channel.close();

+    }

+    public void close(int timeout) {

+        channel.close(timeout);

+    }

+

+    public boolean isClosed() {

+        return channel.isClosed();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerAdapter.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerAdapter.java
new file mode 100644
index 0000000..f77605e
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerAdapter.java
@@ -0,0 +1,44 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * ChannelHandlerAdapter.
+ * 
+ * @author qian.lei
+ */
+public class ChannelHandlerAdapter implements ChannelHandler {
+
+    public void connected(Channel channel) throws RemotingException {
+    }
+
+    public void disconnected(Channel channel) throws RemotingException {
+    }
+
+    public void sent(Channel channel, Object message) throws RemotingException {
+    }
+
+    public void received(Channel channel, Object message) throws RemotingException {
+    }
+
+    public void caught(Channel channel, Throwable exception) throws RemotingException {
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDelegate.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDelegate.java
new file mode 100644
index 0000000..b618761
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDelegate.java
@@ -0,0 +1,25 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;
+
+import com.alibaba.dubbo.remoting.ChannelHandler;
+
+/**
+ * @author chao.liuc
+ */
+public interface ChannelHandlerDelegate extends ChannelHandler {
+    public ChannelHandler getHandler();
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDispatcher.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDispatcher.java
new file mode 100644
index 0000000..76fdd56
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDispatcher.java
@@ -0,0 +1,115 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;

+

+import java.util.Arrays;

+import java.util.Collection;

+import java.util.concurrent.CopyOnWriteArraySet;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+

+/**

+ * ChannelListenerDispatcher

+ * 

+ * @author william.liangf

+ */

+public class ChannelHandlerDispatcher implements ChannelHandler {

+

+    private static final Logger logger = LoggerFactory.getLogger(ChannelHandlerDispatcher.class);

+

+    private final Collection<ChannelHandler> channelHandlers = new CopyOnWriteArraySet<ChannelHandler>();

+    

+    public ChannelHandlerDispatcher() {

+    }

+    

+    public ChannelHandlerDispatcher(ChannelHandler... handlers) {

+        this(handlers == null ? null : Arrays.asList(handlers));

+    }

+

+    public ChannelHandlerDispatcher(Collection<ChannelHandler> handlers) {

+        if (handlers != null && handlers.size() > 0) {

+            this.channelHandlers.addAll(handlers);

+        }

+    }

+

+    public Collection<ChannelHandler> getChannelHandlers() {

+        return channelHandlers;

+    }

+

+    public ChannelHandlerDispatcher addChannelHandler(ChannelHandler handler) {

+        this.channelHandlers.add(handler);

+        return this;

+    }

+

+    public ChannelHandlerDispatcher removeChannelHandler(ChannelHandler handler) {

+        this.channelHandlers.remove(handler);

+        return this;

+    }

+

+    public void connected(Channel channel) {

+        for (ChannelHandler listener : channelHandlers) {

+            try {

+                listener.connected(channel);

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+    }

+

+    public void disconnected(Channel channel) {

+        for (ChannelHandler listener : channelHandlers) {

+            try {

+                listener.disconnected(channel);

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+    }

+

+    public void sent(Channel channel, Object message) {

+        for (ChannelHandler listener : channelHandlers) {

+            try {

+                listener.sent(channel, message);

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+    }

+

+    public void received(Channel channel, Object message) {

+        for (ChannelHandler listener : channelHandlers) {

+            try {

+                listener.received(channel, message);

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+    }

+

+    public void caught(Channel channel, Throwable exception) {

+        for (ChannelHandler listener : channelHandlers) {

+            try {

+                listener.caught(channel, exception);

+            } catch (Throwable t) {

+                logger.error(t.getMessage(), t);

+            }

+        }

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ClientDelegate.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ClientDelegate.java
new file mode 100644
index 0000000..6bb445d
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ClientDelegate.java
@@ -0,0 +1,120 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;

+

+import java.net.InetSocketAddress;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Client;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * ClientDelegate

+ * 

+ * @author william.liangf

+ */

+public class ClientDelegate implements Client {

+    

+    private transient Client client;

+

+    public ClientDelegate() {

+    }

+

+    public ClientDelegate(Client client){

+        setClient(client);

+    }

+    

+    public Client getClient() {

+        return client;

+    }

+    

+    public void setClient(Client client) {

+        if (client == null) {

+            throw new IllegalArgumentException("client == null");

+        }

+        this.client = client;

+    }

+

+    public void reset(URL url) {

+        client.reset(url);

+    }

+    

+    @Deprecated

+    public void reset(com.alibaba.dubbo.common.Parameters parameters){

+        reset(getUrl().addParameters(parameters.getParameters()));

+    }

+

+    public URL getUrl() {

+        return client.getUrl();

+    }

+

+    public InetSocketAddress getRemoteAddress() {

+        return client.getRemoteAddress();

+    }

+

+    public void reconnect() throws RemotingException {

+        client.reconnect();

+    }

+

+    public ChannelHandler getChannelHandler() {

+        return client.getChannelHandler();

+    }

+

+    public boolean isConnected() {

+        return client.isConnected();

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return client.getLocalAddress();

+    }

+

+    public boolean hasAttribute(String key) {

+        return client.hasAttribute(key);

+    }

+

+    public void send(Object message) throws RemotingException {

+        client.send(message);

+    }

+

+    public Object getAttribute(String key) {

+        return client.getAttribute(key);

+    }

+

+    public void setAttribute(String key, Object value) {

+        client.setAttribute(key, value);

+    }

+

+    public void send(Object message, boolean sent) throws RemotingException {

+        client.send(message, sent);

+    }

+

+    public void removeAttribute(String key) {

+        client.removeAttribute(key);

+    }

+

+    public void close() {

+        client.close();

+    }

+    public void close(int timeout) {

+        client.close(timeout);

+    }

+

+    public boolean isClosed() {

+        return client.isClosed();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ServerDelegate.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ServerDelegate.java
new file mode 100644
index 0000000..e91572e
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ServerDelegate.java
@@ -0,0 +1,103 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport;

+

+import java.net.InetSocketAddress;

+import java.util.Collection;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Server;

+

+/**

+ * ServerDelegate

+ * 

+ * @author william.liangf

+ */

+public class ServerDelegate implements Server {

+    

+    private transient Server server;

+    

+    public ServerDelegate() {

+    }

+

+    public ServerDelegate(Server server){

+        setServer(server);

+    }

+

+    public Server getServer() {

+        return server;

+    }

+    

+    public void setServer(Server server) {

+        this.server = server;

+    }

+

+    public boolean isBound() {

+        return server.isBound();

+    }

+

+    public void reset(URL url) {

+        server.reset(url);

+    }

+    

+    @Deprecated

+    public void reset(com.alibaba.dubbo.common.Parameters parameters){

+        reset(getUrl().addParameters(parameters.getParameters()));

+    }

+

+    public Collection<Channel> getChannels() {

+        return server.getChannels();

+    }

+

+    public Channel getChannel(InetSocketAddress remoteAddress) {

+        return server.getChannel(remoteAddress);

+    }

+

+    public URL getUrl() {

+        return server.getUrl();

+    }

+

+    public ChannelHandler getChannelHandler() {

+        return server.getChannelHandler();

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return server.getLocalAddress();

+    }

+

+    public void send(Object message) throws RemotingException {

+        server.send(message);

+    }

+

+    public void send(Object message, boolean sent) throws RemotingException {

+        server.send(message, sent);

+    }

+

+    public void close() {

+        server.close();

+    }

+    

+    public void close(int timeout) {

+        server.close(timeout);

+    }

+

+    public boolean isClosed() {

+        return server.isClosed();

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java
new file mode 100644
index 0000000..8659785
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java
@@ -0,0 +1,64 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.codec;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.transport.AbstractCodec;

+

+/**

+ * TransportCodec

+ * 

+ * @author william.liangf

+ */

+public class TransportCodec extends AbstractCodec {

+

+    public void encode(Channel channel, OutputStream output, Object message) throws IOException {

+        ObjectOutput objectOutput = getSerialization(channel).serialize(channel.getUrl(), output);

+        encodeData(channel, objectOutput, message);

+        objectOutput.flushBuffer();

+    }

+

+    public Object decode(Channel channel, InputStream input) throws IOException {

+        return decodeData(channel, getSerialization(channel).deserialize(channel.getUrl(), input));

+    }

+

+    protected void encodeData(Channel channel, ObjectOutput output, Object message) throws IOException {

+        encodeData(output, message);

+    }

+

+    protected Object decodeData(Channel channel, ObjectInput input) throws IOException {

+        return decodeData(input);

+    }

+

+    protected void encodeData(ObjectOutput output, Object message) throws IOException {

+        output.writeObject(message);

+    }

+

+    protected Object decodeData(ObjectInput input) throws IOException {

+        try {

+            return input.readObject();

+        } catch (ClassNotFoundException e) {

+            throw new IOException("ClassNotFoundException: " + StringUtils.toString(e));

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelEventRunnable.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelEventRunnable.java
new file mode 100644
index 0000000..e637d92
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelEventRunnable.java
@@ -0,0 +1,127 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class ChannelEventRunnable implements Runnable {
+    private final ChannelHandler handler;
+    private final Channel channel;
+    private final ChannelState state;
+    private final Throwable exception;
+    private final Object message;
+    
+    public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state) {
+        this(channel, handler, state, null);
+    }
+    
+    public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Object message) {
+        this(channel, handler, state, message, null);
+    }
+    
+    public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Throwable t) {
+        this(channel, handler, state, null , t);
+    }
+
+    public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Object message, Throwable exception) {
+        this.channel = channel;
+        this.handler = handler;
+        this.state = state;
+        this.message = message;
+        this.exception = exception;
+    }
+    
+    public void run() {
+        switch (state) {
+            case CONNECTED:
+                try{
+                    handler.connected(channel);
+                }catch (Exception e) {
+                    throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel,e);
+                }
+                break;
+            case DISCONNECTED:
+                try{
+                    handler.disconnected(channel);
+                }catch (Exception e) {
+                    throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel,e);
+                }
+                break;
+            case SENT:
+                try{
+                    handler.sent(channel,message);
+                }catch (Exception e) {
+                    throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel+",message is "+ message,e);
+                }
+                
+                break;
+            case RECEIVED:
+                try{
+                    handler.received(channel, message);
+                }catch (Exception e) {
+                    throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel+",message is "+ message,e);
+                }
+                break;
+            case CAUGHT:
+                try{
+                    handler.caught(channel, exception);
+                }catch (Exception e) {
+                    throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel +", message is: "+message+" exception is "+exception,e);
+                }
+                
+                break;
+                }
+        }
+    

+    /**

+     * ChannelState

+     * 

+     * @author william.liangf

+     */
+    public enum ChannelState{

+        

+        /**

+         * CONNECTED

+         */
+        CONNECTED,

+        

+        /**

+         * DISCONNECTED

+         */

+        DISCONNECTED,

+        

+        /**

+         * SENT

+         */

+        SENT,

+        

+        /**

+         * RECEIVED

+         */

+        RECEIVED,

+        

+        /**

+         * CAUGHT

+         */

+        CAUGHT
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelHandlers.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelHandlers.java
new file mode 100644
index 0000000..f733a1a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelHandlers.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather;
+
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Dispather;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class ChannelHandlers {
+
+    public static ChannelHandler wrap(ChannelHandler handler, URL url){
+        return ExtensionLoader.getExtensionLoader(Dispather.class)
+            .getAdaptiveExtension().dispath(handler, url);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/WrappedChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/WrappedChannelHandler.java
new file mode 100644
index 0000000..56b9cb8
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/WrappedChannelHandler.java
@@ -0,0 +1,106 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather;
+
+import java.util.concurrent.ExecutorService;

+import java.util.concurrent.Executors;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.threadpool.ThreadPool;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.exchange.Response;

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerDelegate;

+
+public class WrappedChannelHandler implements ChannelHandlerDelegate {
+    
+    protected static final Logger logger = LoggerFactory.getLogger(WrappedChannelHandler.class);
+
+    protected static final ExecutorService SHARED_EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory("DubboSharedHandler", true));
+    
+    protected final ExecutorService executor;
+    
+    protected final ChannelHandler handler;
+
+    protected final URL url;
+    
+    public WrappedChannelHandler(ChannelHandler handler, URL url) {
+        this.handler = handler;
+        this.url = url;
+        executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
+    }
+    
+    public void close() {
+        try {
+            if (executor instanceof ExecutorService) {
+                ((ExecutorService)executor).shutdown();
+            }
+        } catch (Throwable t) {
+            logger.warn("fail to destroy thread pool of server: " + t.getMessage(), t);
+        }
+    }
+
+    public void connected(Channel channel) throws RemotingException {
+        handler.connected(channel);
+    }
+
+    public void disconnected(Channel channel) throws RemotingException {
+        handler.disconnected(channel);
+    }
+
+    public void sent(Channel channel, Object message) throws RemotingException {
+        handler.sent(channel, message);
+    }
+
+    @SuppressWarnings("deprecation")

+    public void received(Channel channel, Object message) throws RemotingException {

+        if (message instanceof Request && ((Request)message).isHeartbeat()){

+            Request req = (Request) message;

+            if (req.isTwoWay()){

+                Response res = new Response(req.getId(),req.getVersion());

+                res.setHeartbeat(true);

+                channel.send(res);

+            }

+        }

+        handler.received(channel, message);
+    }
+
+    public void caught(Channel channel, Throwable exception) throws RemotingException {
+        handler.caught(channel, exception);
+    }
+    
+    public ExecutorService getExecutor() {
+        return executor;
+    }
+    
+    public ChannelHandler getHandler() {
+        if (handler instanceof ChannelHandlerDelegate) {

+            return ((ChannelHandlerDelegate) handler).getHandler();

+        } else {

+            return handler;

+        }
+    }

+    
+    public URL getUrl() {
+        return url;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllChannelHandler.java
new file mode 100644
index 0000000..4c5828d
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllChannelHandler.java
@@ -0,0 +1,84 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather.all;
+
+import java.util.concurrent.ExecutorService;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.ExecutionException;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable;

+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable.ChannelState;

+
+public class AllChannelHandler extends WrappedChannelHandler {
+    
+    public AllChannelHandler(ChannelHandler handler, URL url) {
+        super(handler, url);
+    }
+
+    public void connected(Channel channel) throws RemotingException {
+        ExecutorService cexecutor = getExecutorService(); 
+        try{
+            cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CONNECTED));
+        }catch (Throwable t) {
+            throw new ExecutionException("connect event", channel, getClass()+" error when process connected event ." , t);
+        }
+    }
+    
+    public void disconnected(Channel channel) throws RemotingException {
+        ExecutorService cexecutor = getExecutorService(); 
+        try{
+            cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.DISCONNECTED));
+        }catch (Throwable t) {
+            throw new ExecutionException("disconnect event", channel, getClass()+" error when process disconnected event ." , t);
+        }
+    }
+
+    public void received(Channel channel, Object message) throws RemotingException {

+      //FIXME 包的依赖顺序有问题

+        if (message instanceof Request && ((Request)message).isEvent()){

+           super.received(channel, message);

+           return;

+        }
+        ExecutorService cexecutor = getExecutorService(); 
+        try{
+            cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+        }catch (Throwable t) {
+            throw new ExecutionException(message, channel, getClass()+" error when process received event ." , t);
+        }
+    }
+
+    public void caught(Channel channel, Throwable exception) throws RemotingException {
+        ExecutorService cexecutor = getExecutorService(); 
+        try{
+            cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CAUGHT, exception));
+        }catch (Throwable t) {
+            throw new ExecutionException("caught event", channel, getClass()+" error when process caught event ." , t);
+        }
+    }
+
+    private ExecutorService getExecutorService() {
+        ExecutorService cexecutor = executor;
+        if (cexecutor == null || cexecutor.isShutdown()) { 
+            cexecutor = SHARED_EXECUTOR;
+        }
+        return cexecutor;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllDispather.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllDispather.java
new file mode 100644
index 0000000..a5f77ed
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllDispather.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather.all;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Dispather;

+
+/**
+ * 默认的线程池配置
+ * 
+ * @author chao.liuc
+ */
+public class AllDispather implements Dispather {
+    
+    public static final String NAME = "all";
+
+    public ChannelHandler dispath(ChannelHandler handler, URL url) {
+        return new AllChannelHandler(handler, url);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedChannelHandler.java
new file mode 100644
index 0000000..6e175e7
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedChannelHandler.java
@@ -0,0 +1,106 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather.connection;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ExecutionException;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable;

+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable.ChannelState;

+
+public class ConnectionOrderedChannelHandler extends WrappedChannelHandler {
+
+    protected final ThreadPoolExecutor connectionExecutor;
+    private final int queuewarninglimit ;
+    
+    public ConnectionOrderedChannelHandler(ChannelHandler handler, URL url) {
+        super(handler, url);
+        String threadName = url.getParameter(Constants.THREAD_NAME_KEY,Constants.DEFAULT_THREAD_NAME);
+        connectionExecutor = new ThreadPoolExecutor(1, 1,
+                                     0L, TimeUnit.MILLISECONDS,
+                                     new LinkedBlockingQueue<Runnable>(url.getPositiveParameter(Constants.CONNECT_QUEUE_CAPACITY, Integer.MAX_VALUE)),
+                                     new NamedThreadFactory(threadName, true),
+                                     new AbortPolicyWithReport(threadName, url)
+            );  // FIXME 没有地方释放connectionExecutor!
+        queuewarninglimit = url.getParameter(Constants.CONNECT_QUEUE_WARNING_SIZE, Constants.DEFAULT_CONNECT_QUEUE_WARNING_SIZE);
+    }
+
+    public void connected(Channel channel) throws RemotingException {
+        try{
+            checkQueueLength();
+            connectionExecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CONNECTED));
+        }catch (Throwable t) {
+            throw new ExecutionException("connect event", channel, getClass()+" error when process connected event ." , t);
+        }
+    }
+
+    public void disconnected(Channel channel) throws RemotingException {
+        try{
+            checkQueueLength();
+            connectionExecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.DISCONNECTED));
+        }catch (Throwable t) {
+            throw new ExecutionException("disconnected event", channel, getClass()+" error when process disconnected event ." , t);
+        }
+    }
+
+    public void received(Channel channel, Object message) throws RemotingException {

+        //FIXME 包的依赖顺序有问题

+        if (message instanceof Request && ((Request)message).isEvent()){

+           super.received(channel, message);

+           return;

+        }

+        
+        ExecutorService cexecutor = executor;
+        if (cexecutor == null || cexecutor.isShutdown()) { 
+            cexecutor = SHARED_EXECUTOR;
+        } 
+        try{
+            cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+        }catch (Throwable t) {
+            throw new ExecutionException(message, channel, getClass()+" error when process received event ." , t);
+        }
+    }
+
+    public void caught(Channel channel, Throwable exception) throws RemotingException {
+        ExecutorService cexecutor = executor;
+        if (cexecutor == null || cexecutor.isShutdown()) { 
+            cexecutor = SHARED_EXECUTOR;
+        } 
+        try{
+            cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CAUGHT, exception));
+        }catch (Throwable t) {
+            throw new ExecutionException("caught event", channel, getClass()+" error when process caught event ." , t);
+        }
+    }
+    
+    private void checkQueueLength(){
+        if (connectionExecutor.getQueue().size() > queuewarninglimit){
+            logger.warn(new IllegalThreadStateException("connectionordered channel handler `queue size: "+connectionExecutor.getQueue().size()+" exceed the warning limit number :"+queuewarninglimit));
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedDispather.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedDispather.java
new file mode 100644
index 0000000..8824299
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedDispather.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather.connection;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Dispather;

+
+/**
+ * connect disconnect 保证顺序.
+ * 
+ * @author chao.liuc
+ */
+public class ConnectionOrderedDispather implements Dispather {
+
+    public static final String NAME = "connection";
+
+    public ChannelHandler dispath(ChannelHandler handler, URL url) {
+        return new ConnectionOrderedChannelHandler(handler, url);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/direct/DirectDispather.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/direct/DirectDispather.java
new file mode 100644
index 0000000..5edbfb7
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/direct/DirectDispather.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather.direct;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Dispather;

+
+/**
+ * 不派发线程池。
+ * 
+ * @author chao.liuc
+ */
+public class DirectDispather implements Dispather {
+    
+    public static final String NAME = "direct";
+
+    public ChannelHandler dispath(ChannelHandler handler, URL url) {
+        return handler;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionChannelHandler.java
new file mode 100644
index 0000000..48ed260
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionChannelHandler.java
@@ -0,0 +1,54 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather.execution;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable;

+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable.ChannelState;

+
+public class ExecutionChannelHandler extends WrappedChannelHandler {
+    
+    public ExecutionChannelHandler(ChannelHandler handler, URL url) {
+        super(handler, url);
+    }
+
+    public void connected(Channel channel) throws RemotingException {
+        executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CONNECTED));
+    }
+
+    public void disconnected(Channel channel) throws RemotingException {
+        executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.DISCONNECTED));
+    }
+
+    public void received(Channel channel, Object message) throws RemotingException {

+      //FIXME 包的依赖顺序有问题

+        if (message instanceof Request && ((Request)message).isEvent()){

+           super.received(channel, message);

+           return;

+        }
+        executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+    }
+
+    public void caught(Channel channel, Throwable exception) throws RemotingException {
+        executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CAUGHT, exception));
+    }

+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionDispather.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionDispather.java
new file mode 100644
index 0000000..64f0901
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionDispather.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather.execution;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Dispather;

+
+/**
+ * 除发送全部使用线程池处理
+ * 
+ * @author chao.liuc
+ */
+public class ExecutionDispather implements Dispather {
+    
+    public static final String NAME = "execution";
+
+    public ChannelHandler dispath(ChannelHandler handler, URL url) {
+        return new ExecutionChannelHandler(handler, url);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyChannelHandler.java
new file mode 100644
index 0000000..b7df172
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyChannelHandler.java
@@ -0,0 +1,54 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather.message;
+
+import java.util.concurrent.ExecutorService;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ExecutionException;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable;

+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;

+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable.ChannelState;

+
+public class MessageOnlyChannelHandler extends WrappedChannelHandler {
+    
+    public MessageOnlyChannelHandler(ChannelHandler handler, URL url) {
+        super(handler, url);
+    }
+
+    public void received(Channel channel, Object message) throws RemotingException {

+        //FIXME 包的依赖顺序有问题

+        if (message instanceof Request && ((Request)message).isEvent()){

+           super.received(channel, message);

+           return;

+        }

+        
+        ExecutorService cexecutor = executor;
+        if (cexecutor == null || cexecutor.isShutdown()) { 
+            cexecutor = SHARED_EXECUTOR;
+        } 
+        try{
+            cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+        }catch (Throwable t) {
+            throw new ExecutionException(message, channel, getClass()+" error when process received event ." , t);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyDispather.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyDispather.java
new file mode 100644
index 0000000..574e8ea
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyDispather.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.transport.dispather.message;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Dispather;

+
+/**
+ * 只有message receive使用线程池.
+ * 
+ * @author chao.liuc
+ */
+public class MessageOnlyDispather implements Dispather {
+
+    public static final String NAME = "message";
+
+    public ChannelHandler dispath(ChannelHandler handler, URL url) {
+        return new MessageOnlyChannelHandler(handler, url);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec b/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec
new file mode 100644
index 0000000..d72ce44
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec
@@ -0,0 +1,3 @@
+transport=com.alibaba.dubbo.remoting.transport.codec.TransportCodec

+telnet=com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec

+exchange=com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Dispather b/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Dispather
new file mode 100644
index 0000000..3eeb5d9
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Dispather
@@ -0,0 +1,5 @@
+all=com.alibaba.dubbo.remoting.transport.dispather.all.AllDispather

+direct=com.alibaba.dubbo.remoting.transport.dispather.direct.DirectDispather

+message=com.alibaba.dubbo.remoting.transport.dispather.message.MessageOnlyDispather

+execution=com.alibaba.dubbo.remoting.transport.dispather.execution.ExecutionDispather

+connection=com.alibaba.dubbo.remoting.transport.dispather.connection.ConnectionOrderedDispather
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger b/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger
new file mode 100644
index 0000000..c07fd67
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger
@@ -0,0 +1 @@
+header=com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler b/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler
new file mode 100644
index 0000000..c56ce82a
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler
@@ -0,0 +1,5 @@
+clear=com.alibaba.dubbo.remoting.telnet.support.command.ClearTelnetHandler

+exit=com.alibaba.dubbo.remoting.telnet.support.command.ExitTelnetHandler

+help=com.alibaba.dubbo.remoting.telnet.support.command.HelpTelnetHandler

+status=com.alibaba.dubbo.remoting.telnet.support.command.StatusTelnetHandler

+log=com.alibaba.dubbo.remoting.telnet.support.command.LogTelnetHandler
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/ChanelHandlerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/ChanelHandlerTest.java
new file mode 100644
index 0000000..1917fd6
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/ChanelHandlerTest.java
@@ -0,0 +1,124 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+
+/**
+ * ChanelHandlerTest
+ * 
+ * mvn clean test -Dtest=*PerformanceClientTest -Dserver=10.20.153.187:9911
+ * 
+ * @author william.liangf
+ */
+public class ChanelHandlerTest extends TestCase {
+    
+    private static final Logger logger = LoggerFactory.getLogger(ChanelHandlerTest.class);
+
+    @Test
+    public void testClient() throws Throwable {
+        // 读取参数
+        if (PerformanceUtils.getProperty("server", null) == null) {
+            logger.warn("Please set -Dserver=127.0.0.1:9911");
+            return;
+        }
+        final String server = System.getProperty("server", "127.0.0.1:9911");
+        final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
+        final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+        final int timeout = PerformanceUtils.getIntProperty(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+        int sleep = PerformanceUtils.getIntProperty("sleep", 60*1000*60);
+        
+        final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization + "&timeout=" + timeout;
+        ExchangeClient exchangeClient =initClient(url);
+        Thread.sleep(sleep);
+        closeClient(exchangeClient);
+    }
+    
+    public static  ExchangeClient initClient(String url){
+        // 创建客户端
+        ExchangeClient exchangeClient  = null;
+        PeformanceTestHandler handler = new PeformanceTestHandler(url);
+        boolean run = true;
+        while(run){
+            try{
+                exchangeClient= Exchangers.connect(url,handler);
+            } catch (Throwable t){
+                
+                if(t!=null && t.getCause()!=null && t.getCause().getClass()!=null && (t.getCause().getClass()==java.net.ConnectException.class 
+                        || t.getCause().getClass()== java.net.ConnectException.class)){
+                    
+                }else {
+                    t.printStackTrace();
+                }
+                
+                try {
+                    Thread.sleep(50);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (exchangeClient != null) {
+                run = false;
+            }
+        }
+        return exchangeClient;
+    }
+    
+    public static void closeClient(ExchangeClient client){
+            if(client.isConnected()){
+                client.close();
+            }
+    }
+    
+    static class PeformanceTestHandler extends ExchangeHandlerAdapter{
+        String url = "";
+        
+        /**
+         * @param url
+         */
+        public PeformanceTestHandler(String url) {
+            this.url = url;
+        }
+
+        public void connected(Channel channel) throws RemotingException {
+            System.out.println("connected event,chanel;"+channel);
+        }
+
+        public void disconnected(Channel channel) throws RemotingException {
+            System.out.println("disconnected event,chanel;"+channel);
+            initClient (url);
+        }
+
+        /* (non-Javadoc)
+         * @see com.alibaba.dubbo.remoting.transport.support.ChannelHandlerAdapter#caught(com.alibaba.dubbo.remoting.Channel, java.lang.Throwable)
+         */
+        @Override
+        public void caught(Channel channel, Throwable exception) throws RemotingException {
+//            System.out.println("caught event:"+exception);
+        }
+        
+        
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoService.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoService.java
new file mode 100644
index 0000000..29cadad
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoService.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+	void sayHello(String name);
+
+	int plus(int a,int b);
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoServiceImpl.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoServiceImpl.java
new file mode 100644
index 0000000..a9a90e6
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoServiceImpl.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+/**
+ * <code>TestServiceImpl</code>
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+	public void sayHello(String name)
+	{
+		System.out.println("hello " + name);
+	}
+
+	public int plus(int a,int b)
+	{
+		return a + b;
+	}
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/Main.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/Main.java
new file mode 100644
index 0000000..f078531
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/Main.java
@@ -0,0 +1,142 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import java.io.Serializable;
+import java.util.Random;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+import com.alibaba.dubbo.remoting.exchange.support.ReplierDispatcher;
+
+/**
+ * Main
+ */
+
+public class Main
+{
+	public static void main(String[] args) throws Exception
+	{
+		startServer(9010);
+		mutliThreadTest(10,9010);
+		dataPackageTest(9010);
+	}
+
+	private static void startServer(int port) throws Exception
+	{
+	    ReplierDispatcher dispatcher = new ReplierDispatcher();
+	    dispatcher.addReplier(RpcMessage.class, new RpcMessageHandler());
+	    dispatcher.addReplier(Object.class, new Replier<Object>() {
+			public Object reply(ExchangeChannel channel, Object msg)
+			{
+				for(int i=0;i<10000;i++)
+					System.currentTimeMillis();
+				System.out.println("handle:"+msg+";thread:"+Thread.currentThread().getName());
+				return new StringMessage("hello world");
+			}
+		});
+		Exchangers.bind(URL.valueOf("dubbo://localhost:" + port), dispatcher);
+	}
+
+	static void dataPackageTest(int port) throws Exception
+	{
+		ExchangeChannel client = Exchangers.connect(URL.valueOf("dubbo://localhost:" + port));
+		Random random = new Random();
+		for(int i=5;i<100;i++)
+		{
+			StringBuilder sb = new StringBuilder();
+			for(int j=0;j<i*100;j++)
+				sb.append("("+random.nextLong()+")");
+			Main.Data d = new Main.Data();
+			d.setData(sb.toString());
+			client.request(d).get();
+		}
+		System.out.println("send finished.");
+	}
+
+	static void mutliThreadTest(int tc,final int port) throws Exception
+	{
+		Executor exec = Executors.newFixedThreadPool(tc);
+		for(int i=0;i<tc;i++)
+			exec.execute(new Runnable(){
+				public void run() {
+					try{ test(port); }catch(Exception e){ e.printStackTrace(); }
+				}
+			});
+	}
+
+	private static void test(int port) throws Exception
+	{
+	    ExchangeChannel client = Exchangers.connect(URL.valueOf("dubbo://localhost:" + port));
+		MockResult result = (MockResult)client.request(new RpcMessage(DemoService.class.getName(),"plus",new Class<?>[]{int.class, int.class},new Object[]{55,25})).get();
+		System.out.println("55+25="+result.getResult());
+
+		for(int i=0;i<100;i++)
+			client.request(new RpcMessage(DemoService.class.getName(),"sayHello", new Class<?>[]{String.class},new Object[]{"qianlei"+i}));
+
+		for(int i=0;i<100;i++)
+			client.request(new Main.Data());
+
+		System.out.println("=====test invoke=====");
+		for(int i=0;i<100;i++){
+			ResponseFuture future = client.request(new Main.Data());
+			System.out.println("invoke and get");
+			System.out.println("invoke result:" + future.get());
+		}
+		System.out.println("=====the end=====");
+	}
+
+	static class Data implements Serializable
+	{
+		private static final long serialVersionUID = -4666580993978548778L;
+
+		private String mData = "";
+
+		public Data(){}
+
+		public String getData()
+		{
+			return mData;
+		}
+
+		public void setData(String data)
+		{
+			mData = data;
+		}
+	}
+
+	static class StringMessage implements Serializable
+	{
+		private static final long serialVersionUID = 7193122183120113947L;
+
+		private String mText;
+
+		StringMessage(String msg)
+		{
+			mText = msg;
+		}
+
+		public String toString()
+		{
+			return mText;
+		}
+	}
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/MockResult.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/MockResult.java
new file mode 100644
index 0000000..e8f9376
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/MockResult.java
@@ -0,0 +1,41 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import java.io.Serializable;
+
+/**
+ * RpcResult.
+ * 
+ * @author qian.lei
+ */
+
+public class MockResult implements Serializable
+{
+	private static final long serialVersionUID = -3630485157441794463L;
+
+	private final Object mResult;
+
+	public MockResult(Object result)
+	{
+		mResult = result;
+	}
+
+	public Object getResult()
+	{
+		return mResult;
+	}
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientCloseTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientCloseTest.java
new file mode 100644
index 0000000..8194562
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientCloseTest.java
@@ -0,0 +1,106 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import java.util.concurrent.atomic.AtomicInteger;

+

+import junit.framework.TestCase;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.Exchangers;

+

+/**

+ * ProformanceClient

+ * 这个测试类会报线程池的异常,因为DefaultChannelHandler中关于线程池的判断产生并发问题(connected事件异步执行,判断已经过了,这时关闭了线程池,然后线程池执行,报错,此问题通过指定Constants.CHANNEL_HANDLER_KEY=connection即可.)

+ * 

+ * @author william.liangf

+ */

+public class PerformanceClientCloseTest extends TestCase {

+

+    private static final Logger logger = LoggerFactory.getLogger(PerformanceClientCloseTest.class);

+

+    @Test

+    public void testClient() throws Throwable {

+        // 读取参数

+        if (PerformanceUtils.getProperty("server", null) == null) {

+            logger.warn("Please set -Dserver=127.0.0.1:9911");

+            return;

+        }

+        final String server = System.getProperty("server", "127.0.0.1:9911");

+        final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);

+        final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);

+        final int timeout = PerformanceUtils.getIntProperty(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);

+        final int concurrent = PerformanceUtils.getIntProperty("concurrent", 1);

+        final int runs = PerformanceUtils.getIntProperty("runs", Integer.MAX_VALUE);

+        final String onerror = PerformanceUtils.getProperty("onerror", "continue");

+        

+        final String url = "exchange://" + server + "?transporter=" + transporter 

+            + "&serialization=" + serialization 

+//            + "&"+Constants.CHANNEL_HANDLER_KEY+"=connection"

+            + "&timeout=" + timeout;

+        

+        final AtomicInteger count = new AtomicInteger();

+        final AtomicInteger error = new AtomicInteger();

+        for (int n = 0; n < concurrent; n++) {

+            new Thread(new Runnable() {

+                public void run() {

+                    for (int i = 0; i < runs; i++) {

+                        ExchangeClient client = null;

+                        try {

+                            client = Exchangers.connect(url);

+                            int c = count.incrementAndGet();

+                            if (c % 100 == 0) {

+                                System.out.println("count: " + count.get() + ", error: " + error.get());

+                            }

+                        } catch (Exception e) {

+                            error.incrementAndGet();

+                            e.printStackTrace();

+                            System.out.println("count: " + count.get() + ", error: " + error.get());

+                            if ("exit".equals(onerror)) {

+                                System.exit(-1);

+                            } else if ("break".equals(onerror)) {

+                                break;

+                            } else if ("sleep".equals(onerror)) {

+                                try {

+                                    Thread.sleep(30000);

+                                } catch (InterruptedException e1) {

+                                }

+                            }

+                        } finally {

+                            if (client != null) {

+                                client.close();

+                            }

+                        }

+                    }

+                }

+            }).start();

+        }

+        synchronized (PerformanceServerTest.class) {

+            while (true) {

+                try {

+                    PerformanceServerTest.class.wait();

+                } catch (InterruptedException e) {

+                }

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientFixedTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientFixedTest.java
new file mode 100644
index 0000000..6b93ca6
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientFixedTest.java
@@ -0,0 +1,136 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import java.util.ArrayList;

+import java.util.Random;

+

+import junit.framework.TestCase;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.Exchangers;

+

+public class PerformanceClientFixedTest extends TestCase {

+

+    private static final Logger logger = LoggerFactory.getLogger(PerformanceClientTest.class);

+

+    @Test

+    public void testClient() throws Exception {

+        // 读取参数

+        if (PerformanceUtils.getProperty("server", null) == null) {

+            logger.warn("Please set -Dserver=127.0.0.1:9911");

+            return;

+        }

+        final String server = System.getProperty("server", "127.0.0.1:9911");

+        final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);

+        final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);

+        final int timeout = PerformanceUtils.getIntProperty(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);

+        //final int length = PerformanceUtils.getIntProperty("length", 1024);

+        final int connectionCount = PerformanceUtils.getIntProperty(Constants.CONNECTIONS_KEY, 1);

+        //final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100);

+        //int r = PerformanceUtils.getIntProperty("runs", 10000);

+        //final int runs = r > 0 ? r : Integer.MAX_VALUE;

+        //final String onerror = PerformanceUtils.getProperty("onerror", "continue");

+        final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization + "&timeout=" + timeout;

+        

+        //int idx = server.indexOf(':');

+        Random rd = new Random(connectionCount);

+        ArrayList<ExchangeClient> arrays          = new ArrayList<ExchangeClient>();

+        String            oneKBlock       = null;

+        String            messageBlock    = null;

+        int s = 0;

+        int f = 0;

+        System.out.println("initialize arrays " + url);

+        while (s < connectionCount) {

+            ExchangeClient client = null;

+            try {

+                System.out.println("open connection " + s + " " + url + arrays.size());

+

+                client = Exchangers.connect(url);

+

+                System.out.println("run after open");

+

+                if (client.isConnected()) {

+                    arrays.add(client);

+                    s++;

+                    System.out.println("open client success " + s);

+                } else {

+                    System.out.println("open client failed, try again.");

+                }

+            } catch (Throwable t) {

+                t.printStackTrace();

+            } finally {

+                if (client != null && client.isConnected() == false) {

+                    f++;

+                    System.out.println("open client failed, try again " + f);

+                    client.close();

+                }

+            }

+        }

+

+        StringBuilder sb1 = new StringBuilder();

+        Random rd2 = new Random();

+        char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();

+        int size1 = numbersAndLetters.length;

+        for (int j = 0; j < 1024; j++) {

+            sb1.append(numbersAndLetters[rd2.nextInt(size1)]);

+        }

+        oneKBlock = sb1.toString();

+

+        for (int j = 0; j < Integer.MAX_VALUE; j++) {

+            try {

+                String size = "10";

+    

+                int request_size = 10;

+                try {

+                    request_size = Integer.parseInt(size);

+                } catch (Throwable t) {

+                    request_size = 10;

+                }

+    

+                if (messageBlock == null) {

+                    StringBuilder sb = new StringBuilder();

+                    for (int i = 0; i < request_size; i++) {

+                        sb.append(oneKBlock);

+                    }

+                    messageBlock = sb.toString();

+    

+                    System.out.println("set messageBlock to " + messageBlock);

+                }

+                int index = rd.nextInt(connectionCount);

+                ExchangeClient client = arrays.get(index);

+                // ExchangeClient client = arrays.get(0);

+                String output = (String) client.request(messageBlock).get();

+    

+                if (output.lastIndexOf(messageBlock) < 0) {

+                    System.out.println("send messageBlock;get " + output);

+                    throw new Throwable("return results invalid");

+                } else {

+                    if (j % 100 == 0)

+                    System.out.println("OK: " + j);

+                }

+            } catch (Throwable t) {

+                t.printStackTrace();

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientMain.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientMain.java
new file mode 100644
index 0000000..1819601
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientMain.java
@@ -0,0 +1,28 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+
+/**
+ * PerformanceClientMain
+ */
+public class PerformanceClientMain {
+
+    public static void main(String[] args) throws Throwable {
+        new PerformanceClientTest().testClient();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientTest.java
new file mode 100644
index 0000000..658de26
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientTest.java
@@ -0,0 +1,228 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import java.text.DecimalFormat;

+import java.text.SimpleDateFormat;

+import java.util.Date;

+import java.util.List;

+import java.util.concurrent.CountDownLatch;

+import java.util.concurrent.atomic.AtomicInteger;

+import java.util.concurrent.atomic.AtomicLong;

+

+import junit.framework.TestCase;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.Exchangers;

+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;

+

+/**

+ * PerformanceClientTest

+ * 

+ * mvn clean test -Dtest=*PerformanceClientTest -Dserver=10.20.153.187:9911

+ * 

+ * @author william.liangf

+ */

+public class PerformanceClientTest extends TestCase {

+    

+    private static final Logger logger = LoggerFactory.getLogger(PerformanceClientTest.class);

+

+    @Test

+    @SuppressWarnings("unchecked")

+    public void testClient() throws Throwable {

+        // 读取参数

+        if (PerformanceUtils.getProperty("server", null) == null) {

+            logger.warn("Please set -Dserver=127.0.0.1:9911");

+            return;

+        }

+        final String server = System.getProperty("server", "127.0.0.1:9911");

+        final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);

+        final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);

+        final int timeout = PerformanceUtils.getIntProperty(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);

+        final int length = PerformanceUtils.getIntProperty("length", 1024);

+        final int connections = PerformanceUtils.getIntProperty(Constants.CONNECTIONS_KEY, 1);

+        final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100);

+        int r = PerformanceUtils.getIntProperty("runs", 10000);

+        final int runs = r > 0 ? r : Integer.MAX_VALUE;

+        final String onerror = PerformanceUtils.getProperty("onerror", "continue");

+        

+        final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization + "&timeout=" + timeout;

+        // 创建客户端

+        final ExchangeClient[] exchangeClients = new ExchangeClient[connections];

+        for (int i = 0; i < connections; i ++) {

+            //exchangeClients[i] = Exchangers.connect(url,handler);

+            exchangeClients[i] = Exchangers.connect(url);

+        }

+        

+        List<String> serverEnvironment = (List<String>) exchangeClients[0].request("environment").get();

+        List<String> serverScene = (List<String>) exchangeClients[0].request("scene").get();

+        

+        // 制造数据

+        StringBuilder buf = new StringBuilder(length);

+        for (int i = 0; i < length; i ++) {

+            buf.append("A");

+        }

+        final String data = buf.toString();

+        

+        // 计数器

+        final AtomicLong count = new AtomicLong();

+        final AtomicLong error = new AtomicLong();

+        final AtomicLong time = new AtomicLong();

+        final AtomicLong all = new AtomicLong();

+        

+        // 并发调用

+        final CountDownLatch latch = new CountDownLatch(concurrent);

+        for (int i = 0; i < concurrent; i ++) {

+            new Thread(new Runnable() {

+                public void run() {

+                    try {

+                        AtomicInteger index = new AtomicInteger();

+                        long init = System.currentTimeMillis();

+                        for (int i = 0; i < runs; i++) {

+                            try {

+                                count.incrementAndGet();

+                                ExchangeClient client = exchangeClients[index.getAndIncrement() % connections];

+                                long start = System.currentTimeMillis();

+                                String result = (String) client.request(data).get();

+                                long end = System.currentTimeMillis();

+                                if (! data.equals(result)) {

+                                    throw new IllegalStateException("Invalid result " + result);

+                                }

+                                time.addAndGet(end - start);

+                            } catch (Exception e) {

+                                error.incrementAndGet();

+                                e.printStackTrace();

+                                if ("exit".equals(onerror)) {

+                                    System.exit(-1);

+                                } else if ("break".equals(onerror)) {

+                                    break;

+                                } else if ("sleep".equals(onerror)) {

+                                    try {

+                                        Thread.sleep(30000);

+                                    } catch (InterruptedException e1) {

+                                    }

+                                }

+                            }

+                        }

+                        all.addAndGet(System.currentTimeMillis() - init);

+                    } finally {

+                        latch.countDown();

+                    }

+                }

+            }).start();

+        }

+        

+        // 输出,tps不精确,但大概反映情况

+        new Thread(new Runnable() {

+            public void run() {

+                try{

+                    SimpleDateFormat dateFormat = new SimpleDateFormat ("HH:mm:ss");

+                    long lastCount = count.get();

+                    long sleepTime = 2000;

+                    long elapsd = sleepTime/1000;

+                    boolean bfirst = true;

+                    while (latch.getCount() > 0) {

+                        long c = count.get()-lastCount ;

+                        if(! bfirst)//第一次不准

+                            System.out.println("["+dateFormat.format(new Date()) +"] count: " + count.get() + ", error: " + error.get() + ",tps:"+(c/elapsd));

+                        

+                        bfirst = false;

+                        lastCount = count.get();

+                        Thread.sleep(sleepTime);

+                    } 

+                } catch(Exception e) {

+                    e.printStackTrace();

+                }

+            }

+        }).start();

+        

+        latch.await();

+        

+        for(ExchangeClient client:exchangeClients){

+            if(client.isConnected()){

+                client.close();

+            }

+        }

+        

+        long total = count.get();

+        long failed = error.get();

+        long succeeded = total - failed;

+        long elapsed = time.get();

+        long allElapsed = all.get();

+        long clientElapsed = allElapsed - elapsed;

+        long art = 0;

+        long qps = 0;

+        long throughput = 0;

+        if (elapsed > 0) {

+            art = elapsed / succeeded;

+            qps = concurrent * succeeded * 1000 / elapsed;

+            throughput = concurrent * succeeded * length * 2 * 1000 / elapsed;

+        }

+        

+        PerformanceUtils.printBorder();

+        PerformanceUtils.printHeader("Dubbo Remoting Performance Test Report");

+        PerformanceUtils.printBorder();

+        PerformanceUtils.printHeader("Test Environment");

+        PerformanceUtils.printSeparator();

+        for (String item: serverEnvironment) {

+            PerformanceUtils.printBody("Server " + item);

+        }

+        PerformanceUtils.printSeparator();

+        List<String> clientEnvironment = PerformanceUtils.getEnvironment();

+        for (String item: clientEnvironment) {

+            PerformanceUtils.printBody("Client " + item);

+        }

+        PerformanceUtils.printSeparator();

+        PerformanceUtils.printHeader("Test Scene");

+        PerformanceUtils.printSeparator();

+        for (String item: serverScene) {

+            PerformanceUtils.printBody("Server " + item);

+        }

+        PerformanceUtils.printBody("Client Transporter: " + transporter);

+        PerformanceUtils.printBody("Serialization: " + serialization);

+        PerformanceUtils.printBody("Response Timeout: " + timeout + " ms");

+        PerformanceUtils.printBody("Data Length: " + length + " bytes");

+        PerformanceUtils.printBody("Client Shared Connections: " + connections);

+        PerformanceUtils.printBody("Client Concurrent Threads: " + concurrent);

+        PerformanceUtils.printBody("Run Times Per Thread: " + runs);

+        PerformanceUtils.printSeparator();

+        PerformanceUtils.printHeader("Test Result");

+        PerformanceUtils.printSeparator();

+        PerformanceUtils.printBody("Succeeded Requests: " + DecimalFormat.getIntegerInstance().format(succeeded));

+        PerformanceUtils.printBody("Failed Requests: " + failed);

+        PerformanceUtils.printBody("Client Elapsed Time: " + clientElapsed + " ms");

+        PerformanceUtils.printBody("Average Response Time: " + art + " ms");

+        PerformanceUtils.printBody("Requests Per Second: " + qps + "/s");

+        PerformanceUtils.printBody("Throughput Per Second: " + DecimalFormat.getIntegerInstance().format(throughput) + " bytes/s");

+        PerformanceUtils.printBorder();

+    }

+    

+    static class PeformanceTestHandler extends ExchangeHandlerAdapter{

+

+        public void connected(Channel channel) throws RemotingException {

+            System.out.println("connected event,chanel;"+channel);

+        }

+

+        public void disconnected(Channel channel) throws RemotingException {

+            System.out.println("disconnected event,chanel;"+channel);

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerMain.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerMain.java
new file mode 100644
index 0000000..55038a3
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerMain.java
@@ -0,0 +1,29 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+/**
+ * PerformanceServerMain
+ * 
+ * @author william.liangf
+ */
+public class PerformanceServerMain{
+    
+    public static void main(String[] args) throws Exception {
+        new PerformanceServerTest().testServer();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerTest.java
new file mode 100644
index 0000000..3d117a7
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerTest.java
@@ -0,0 +1,163 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import com.alibaba.dubbo.remoting.transport.dispather.execution.ExecutionDispather;

+
+/**
+ * PerformanceServer
+ * 
+ * mvn clean test -Dtest=*PerformanceServerTest -Dport=9911
+ * 
+ * @author william.liangf
+ */
+public class PerformanceServerTest extends TestCase {
+
+    private static final Logger logger = LoggerFactory.getLogger(PerformanceServerTest.class);
+    private static ExchangeServer server = null;
+    @Test
+    public void testServer() throws Exception {
+        // 读取参数
+        if (PerformanceUtils.getProperty("port", null) == null) {
+            logger.warn("Please set -Dport=9911");
+            return;
+        }
+        final int port = PerformanceUtils.getIntProperty("port", 9911);
+        final boolean telnet = PerformanceUtils.getBooleanProperty("telnet", true);
+        if(telnet)statTelnetServer(port+1);
+        server = statServer();
+        
+        synchronized (PerformanceServerTest.class) {
+            while (true) {
+                try {
+                    PerformanceServerTest.class.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+    private static void restartServer(int times,int alive,int sleep) throws Exception{
+        if(server!=null && !server.isClosed()){
+            server.close();
+            Thread.sleep(100);
+        }
+        
+        for(int i=0;i<times;i++){
+            logger.info("restart times:"+i);
+            server = statServer();
+            if(alive>0)Thread.sleep(alive);
+            server.close();
+            if(sleep>0)Thread.sleep(sleep);
+        }
+        
+        server = statServer();
+    }
+    
+    private static ExchangeServer statServer() throws Exception{
+        final int port = PerformanceUtils.getIntProperty("port", 9911);
+        final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
+        final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+        final String threadpool = PerformanceUtils.getProperty(Constants.THREADPOOL_KEY, Constants.DEFAULT_THREADPOOL);
+        final int threads = PerformanceUtils.getIntProperty(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
+        final int iothreads = PerformanceUtils.getIntProperty(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS);
+        final int buffer = PerformanceUtils.getIntProperty(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);
+        final String channelHandler = PerformanceUtils.getProperty(Constants.CHANNEL_HANDLER_KEY, ExecutionDispather.NAME);
+        
+        
+        // 启动服务器
+        ExchangeServer server = Exchangers.bind("exchange://0.0.0.0:" + port + "?transporter=" 
+                        + transporter + "&serialization=" 
+                        + serialization + "&threadpool=" + threadpool 
+                        + "&threads=" + threads + "&iothreads=" + iothreads +"&buffer="+buffer +"&channel.handler="+channelHandler, new ExchangeHandlerAdapter() {
+            public String telnet(Channel channel, String message) throws RemotingException {
+                 return "echo: " + message + "\r\ntelnet> ";
+            }
+            public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
+                if ("environment".equals(request)) {
+                    return PerformanceUtils.getEnvironment();
+                }
+                if ("scene".equals(request)) {
+                    List<String> scene = new ArrayList<String>();
+                    scene.add("Transporter: " + transporter);
+                    scene.add("Service Threads: " + threads);
+                    return scene;
+                }
+                return request;
+            }
+        });
+        
+        return server;
+    }
+    
+    private static ExchangeServer statTelnetServer(int port) throws Exception{
+       // 启动服务器
+        ExchangeServer telnetserver = Exchangers.bind("exchange://0.0.0.0:" + port , new ExchangeHandlerAdapter() {
+            public String telnet(Channel channel, String message) throws RemotingException {
+                if(message.equals("help")){
+                    return "support cmd: \r\n\tstart \r\n\tstop \r\n\tshutdown \r\n\trestart times [alive] [sleep] \r\ntelnet>";
+                } else if(message.equals("stop")){
+                    logger.info("server closed:"+server);
+                    server.close();                    
+                    return "stop server\r\ntelnet>";
+                } else if(message.startsWith("start")){
+                    try {
+                        restartServer(0,0,0);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                    return "start server\r\ntelnet>";
+                } else if(message.startsWith("shutdown")){
+                        System.exit(0);
+                    return "start server\r\ntelnet>";
+                } else if(message.startsWith("channels")){
+                    return "server.getExchangeChannels():"+server.getExchangeChannels().size()+"\r\ntelnet>";
+                } else if(message.startsWith("restart ")){ //r times [sleep] r 10 or r 10 100
+                    String[] args = message.split(" ");
+                    int times = Integer.parseInt(args[1]);
+                    int alive = args.length>2?Integer.parseInt(args[2]):0;
+                    int sleep = args.length>3?Integer.parseInt(args[3]):100;
+                    try {
+                        restartServer(times,alive,sleep);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                    
+                    return "restart server,times:"+times+" stop alive time: "+alive+",sleep time: " + sleep+" usage:r times [alive] [sleep] \r\ntelnet>";
+                } else{
+                    return "echo: " + message + "\r\ntelnet> ";
+                }
+                
+            }
+        });
+        
+        return telnetserver;
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceUtils.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceUtils.java
new file mode 100644
index 0000000..f74cc8c
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceUtils.java
@@ -0,0 +1,126 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import java.net.NetworkInterface;

+import java.net.SocketException;

+import java.text.DecimalFormat;

+import java.util.ArrayList;

+import java.util.Enumeration;

+import java.util.List;

+

+/**

+ * PerformanceUtils

+ * 

+ * @author william.liangf

+ */

+public class PerformanceUtils {

+

+    public static String getProperty(String key, String defaultValue) {

+        String value = System.getProperty(key);

+        if (value == null || value.trim().length() == 0 || value.startsWith("$")) {

+            return defaultValue;

+        }

+        return value.trim();

+    }

+    

+    public static int getIntProperty(String key, int defaultValue) {

+        String value = System.getProperty(key);

+        if (value == null || value.trim().length() == 0 || value.startsWith("$")) {

+            return defaultValue;

+        }

+        return Integer.parseInt(value.trim());

+    }

+    public static boolean getBooleanProperty(String key, boolean defaultValue) {

+        String value = System.getProperty(key);

+        if (value == null || value.trim().length() == 0 || value.startsWith("$")) {

+            return defaultValue;

+        }

+        return Boolean.parseBoolean(value.trim());

+    }

+    

+    public static List<String> getEnvironment() {

+        List<String> environment = new ArrayList<String>();

+        environment.add("OS: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch", ""));

+        environment.add("CPU: " + Runtime.getRuntime().availableProcessors() + " cores");

+        environment.add("JVM: " + System.getProperty("java.vm.name") + " " + System.getProperty("java.runtime.version"));

+        environment.add("Memory: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().totalMemory()) 

+                                   + " bytes (Max: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().maxMemory()) + " bytes)");

+        NetworkInterface ni = PerformanceUtils.getNetworkInterface();

+        if (ni != null) {

+            environment.add("Network: " + ni.getDisplayName());

+        }

+        return environment;

+    }

+

+    private static final int WIDTH = 64;

+    

+    public static void printSeparator() {

+        StringBuilder pad = new StringBuilder();

+        for (int i = 0; i < WIDTH; i ++) {

+            pad.append("-");

+        }

+        System.out.println("+" + pad + "+");

+    }

+

+    public static void printBorder() {

+        StringBuilder pad = new StringBuilder();

+        for (int i = 0; i < WIDTH; i ++) {

+            pad.append("=");

+        }

+        System.out.println("+" + pad + "+");

+    }

+   

+    public static void printBody(String msg) {

+        StringBuilder pad = new StringBuilder();

+        int len = WIDTH - msg.length() - 1;

+        if (len > 0) {

+            for (int i = 0; i < len; i ++) {

+                pad.append(" ");

+            }

+        }

+        System.out.println("| " + msg + pad + "|");

+    }

+    

+    public static void printHeader(String msg) {

+        StringBuilder pad = new StringBuilder();

+        int len = WIDTH - msg.length();

+        if (len > 0) {

+            int half = len / 2;

+            for (int i = 0; i < half; i ++) {

+                pad.append(" ");

+            }

+        }

+        System.out.println("|" + pad + msg + pad + ((len % 2 == 0) ? "" : " ") + "|");

+    }

+    

+    public static NetworkInterface getNetworkInterface() {

+        try {

+            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();

+            if (interfaces != null) {

+                while (interfaces.hasMoreElements()) {

+                    try {

+                        return interfaces.nextElement();

+                    } catch (Throwable e) {

+                    }

+                }

+            }

+        } catch (SocketException e) {

+        }

+        return null;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessage.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessage.java
new file mode 100755
index 0000000..8ab2c36
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessage.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import java.io.Serializable;

+

+/**

+ * RpcMessage.

+ * 

+ * @author qian.lei

+ */

+

+public class RpcMessage implements Serializable

+{

+	private static final long serialVersionUID = -5148079121106659095L;

+

+	private String mClassName;

+

+	private String mMethodDesc;

+

+	private Class<?>[] mParameterTypes;

+

+	private Object[] mArguments;

+

+	public RpcMessage(String cn, String desc, Class<?>[] parameterTypes,Object[] args)

+	{

+		mClassName = cn;

+		mMethodDesc = desc;

+		mParameterTypes = parameterTypes;

+		mArguments = args;

+	}

+

+	public String getClassName()

+	{

+		return mClassName;

+	}

+

+	public String getMethodDesc()

+	{

+		return mMethodDesc;

+	}

+

+	public Class<?>[] getParameterTypes() {

+		return mParameterTypes;

+	}

+

+	public Object[] getArguments()

+	{

+		return mArguments;

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessageHandler.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessageHandler.java
new file mode 100755
index 0000000..0a12593
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessageHandler.java
@@ -0,0 +1,90 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import java.lang.reflect.InvocationTargetException;

+

+import com.alibaba.dubbo.common.bytecode.NoSuchMethodException;

+import com.alibaba.dubbo.common.bytecode.Wrapper;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.support.Replier;

+

+/**

+ * RpcMessageHandler.

+ * 

+ * @author qian.lei

+ */

+

+public class RpcMessageHandler implements Replier<RpcMessage>

+{

+	public static interface ServiceProvider{ Object getImplementation(String service); }

+

+	private final static ServiceProvider DEFAULT_PROVIDER = new ServiceProvider(){

+		public Object getImplementation(String service)

+		{

+			String impl = service + "Impl";

+			try

+			{

+				Class<?> cl = Thread.currentThread().getContextClassLoader().loadClass(impl);

+				return cl.newInstance();

+			}

+			catch(Exception e)

+			{

+				e.printStackTrace();

+			}

+			return null;

+		}

+	};

+

+	private ServiceProvider mProvider;

+

+	public RpcMessageHandler()

+	{

+		this(DEFAULT_PROVIDER);

+	}

+

+	public RpcMessageHandler(ServiceProvider prov)

+	{

+		mProvider = prov;

+	}

+

+	public Class<RpcMessage> interest()

+	{

+		return RpcMessage.class;

+	}

+

+	public Object reply(ExchangeChannel channel, RpcMessage msg) throws RemotingException

+	{

+		String desc = msg.getMethodDesc();

+		Object[] args = msg.getArguments();

+		Object impl = mProvider.getImplementation(msg.getClassName());

+		Wrapper wrap = Wrapper.getWrapper(impl.getClass());

+		try

+		{

+			return new MockResult(wrap.invokeMethod(impl, desc, msg.getParameterTypes(), args));

+		}

+		catch(NoSuchMethodException e)

+		{

+			throw new RemotingException(channel, "Service method not found.");

+		}

+		catch(InvocationTargetException e)

+		{

+			return new MockResult(e.getTargetException());

+		}

+		

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/TelnetServer.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/TelnetServer.java
new file mode 100644
index 0000000..637c9ce
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/TelnetServer.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting;

+

+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;

+

+/**

+ * TelnetServer

+ * 

+ * @author william.liangf

+ */

+public class TelnetServer {

+

+    public static void main(String[] args) throws Exception {

+        Transporters.bind("telnet://0.0.0.0:23", new ChannelHandlerAdapter() {

+            public void connected(Channel channel) throws RemotingException {

+                channel.send("telnet> ");

+            }

+            public void received(Channel channel, Object message) throws RemotingException {

+                channel.send("Echo: " + message + "\r\n");

+                channel.send("telnet> ");

+            }

+        });

+        // 阻止JVM退出

+        synchronized (TelnetServer.class) {

+            while (true) {

+                try {

+                    TelnetServer.class.wait();

+                } catch (InterruptedException e) {

+                }

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/AbstractMockChannel.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/AbstractMockChannel.java
new file mode 100644
index 0000000..325a50c
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/AbstractMockChannel.java
@@ -0,0 +1,121 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.codec;
+
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class AbstractMockChannel implements Channel {
+    public static final String LOCAL_ADDRESS = "local";
+    public static final String REMOTE_ADDRESS = "remote";
+    public static final String ERROR_WHEN_SEND = "error_when_send";
+    private URL remoteUrl ;
+    InetSocketAddress localAddress ;
+    InetSocketAddress remoteAddress ;
+    private ChannelHandler handler;
+    private boolean isClosed ;
+    private Map<String, Object> attributes = new HashMap<String, Object>(1);
+    private volatile Object receivedMessage = null;
+    
+    public AbstractMockChannel(){
+        
+    }
+    
+    public AbstractMockChannel(URL remoteUrl){
+        this.remoteUrl = remoteUrl;
+        this.remoteAddress = NetUtils.toAddress(remoteUrl.getParameter(REMOTE_ADDRESS));
+        this.localAddress = NetUtils.toAddress(remoteUrl.getParameter(LOCAL_ADDRESS));
+    }
+    public AbstractMockChannel(ChannelHandler handler){
+        this.handler = handler;
+    }
+
+    public URL getUrl() {
+        return remoteUrl;
+    }
+
+    public ChannelHandler getChannelHandler() {
+        return handler;
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        return localAddress ;
+    }
+
+    public void send(Object message) throws RemotingException {
+        if (remoteUrl.getParameter(ERROR_WHEN_SEND, Boolean.FALSE)){
+            receivedMessage = null ;
+            throw new RemotingException(localAddress, remoteAddress, "mock error");
+        } else {
+            receivedMessage = message;
+        }
+    }
+    
+    public void send(Object message, boolean sent) throws RemotingException {
+        send(message);
+    }
+
+    public void close() {
+        close(0);
+    }
+
+    public void close(int timeout) {
+        isClosed = true;
+    }
+
+    public boolean isClosed() {
+        return isClosed;
+    }
+
+    public InetSocketAddress getRemoteAddress() {
+        return remoteAddress;
+    }
+
+    public boolean isConnected() {
+        return isClosed;
+    }
+
+    public boolean hasAttribute(String key) {
+        return attributes.containsKey(key);
+    }
+
+    public Object getAttribute(String key) {
+        return attributes.get(key);
+    }
+
+    public void setAttribute(String key, Object value) {
+        attributes.put(key, value);
+    }
+
+    public void removeAttribute(String key) {
+        attributes.remove(key);
+    }
+
+    public Object getReceivedMessage() {
+        return receivedMessage;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/ExchangeCodecTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/ExchangeCodecTest.java
new file mode 100644
index 0000000..44536cb
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/ExchangeCodecTest.java
@@ -0,0 +1,386 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.codec;
+
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec;
+import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec;
+
+/**
+ * @author chao.liuc
+ * byte 16
+ * 0-1 magic code
+ * 2 flag 
+ *      8 - 1-request/0-response
+ *      7 - two way
+ *      6 - heartbeat
+ *      1-5 serialization id
+ * 3 status 
+ *      20 ok
+ *      90 error?
+ * 4-11 id (long)
+ * 12 -15 datalength
+ *
+ */
+public class ExchangeCodecTest extends TelnetCodecTest{
+    // magic header.
+    private static final short    MAGIC              = (short) 0xdabb;
+    private static final byte     MAGIC_HIGH         = (byte) Bytes.short2bytes(MAGIC)[0];
+    private static final byte     MAGIC_LOW          = (byte) Bytes.short2bytes(MAGIC)[1];
+    Serialization serialization = getSerialization(Constants.DEFAULT_REMOTING_SERIALIZATION);
+    
+    
+
+    private Object decode(byte[] request) throws IOException{
+        InputStream input = new UnsafeByteArrayInputStream(request);
+        AbstractMockChannel channel = getServerSideChannel(url);
+        //decode
+        Object obj = codec.decode(channel, input);
+        return obj;
+    }
+    
+    private byte[] getRequestBytes(Object obj, byte[] header) throws IOException{
+        // encode request data.
+        UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+        ObjectOutput out = serialization.serialize(url, bos);
+        out.writeObject(obj);
+        
+        out.flushBuffer();
+        bos.flush();
+        bos.close();
+        byte[] data = bos.toByteArray();
+        byte[] len = Bytes.int2bytes(data.length);
+        System.arraycopy(len, 0, header, 12, 4);
+        byte[] request = join(header, data);
+        return request;
+    }
+    
+    private byte[] assemblyDataProtocol(byte[] header){
+        Person request = new Person();
+        byte[] newbuf = join(header, objectToByte(request));
+        return newbuf;
+    }
+    
+    private static Serialization getSerialization(String name) {
+        Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name);
+        return serialization;
+    }
+    //===================================================================================
+    
+    @Before
+    public void setUp() throws Exception {
+        codec = new ExchangeCodec();
+    }
+    @Test
+    public void test_Decode_Error_MagicNum() throws IOException{
+        HashMap<byte[] , Object> inputBytes = new HashMap<byte[] , Object>();
+        inputBytes.put( new byte[] { 0 }, TelnetCodec.NEED_MORE_INPUT ); 
+        inputBytes.put( new byte[] { MAGIC_HIGH, 0 }, TelnetCodec.NEED_MORE_INPUT );
+        inputBytes.put( new byte[] { 0 , MAGIC_LOW }, TelnetCodec.NEED_MORE_INPUT );
+        
+        for (byte[] input : inputBytes.keySet()){
+            testDecode_assertEquals(assemblyDataProtocol(input) ,inputBytes.get(input));
+        }
+    }
+    
+    @Test
+    public void test_Decode_Error_Length() throws IOException{
+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0x20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+        Person person = new Person();
+        byte[] request = getRequestBytes(person, header);
+        
+        Channel channel = getServerSideChannel(url);
+        byte[] baddata = new byte[]{1,2};
+        UnsafeByteArrayInputStream input = new UnsafeByteArrayInputStream(join(request, baddata));
+        Response obj = (Response)codec.decode(channel, input);
+        Assert.assertEquals(person, obj.getResult());
+        System.out.println(input.available());
+        //only decode necessary bytes
+        Assert.assertEquals(request.length, input.position());
+    }
+    @Test
+    public void test_Decode_Error_Response_Object() throws IOException{
+        //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0x20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+        Person person = new Person();
+        byte[] request = getRequestBytes(person, header);
+        //bad object
+        byte[] badbytes = new byte[]{-1,-2,-3,-4,-3,-4,-3,-4,-3,-4,-3,-4};
+        System.arraycopy(badbytes, 0, request, 21, badbytes.length);
+        
+        Response obj = (Response)decode(request);
+        Assert.assertEquals(90, obj.getStatus());
+    }
+    @Test
+    public void test_Decode_Check_Payload() throws IOException{
+        byte[] header = new byte[] { MAGIC_HIGH , MAGIC_LOW , 1 ,1 ,1 ,1 ,1 , 1 ,1 ,1 ,1 ,1 , 1 ,1 , 1,1 };
+        byte[] request = assemblyDataProtocol(header) ;
+        try{
+            testDecode_assertEquals(request, TelnetCodec.NEED_MORE_INPUT);
+            fail();
+        }catch (IOException expected) {
+            Assert.assertTrue(expected.getMessage().startsWith("Data length too large: "+Bytes.bytes2int(new byte[]{1,1,1,1})));
+        }
+    }
+    @Test
+    public void test_Decode_Header_Need_Readmore() throws IOException{
+        byte[] header = new byte[] { MAGIC_HIGH , MAGIC_LOW , 0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0  };
+        testDecode_assertEquals(header, TelnetCodec.NEED_MORE_INPUT);
+    }
+    
+    @Test
+    public void test_Decode_Body_Need_Readmore() throws IOException{
+        byte[] header = new byte[] { MAGIC_HIGH , MAGIC_LOW , 0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0, 0, 0, 0 , 1 ,1, 'a', 'a'  };
+        testDecode_assertEquals(header, TelnetCodec.NEED_MORE_INPUT);
+    }
+    @Test
+    public void test_Decode_MigicCodec_Contain_ExchangeHeader() throws IOException{
+        //
+        byte[] header = new byte[] { 0, 0, MAGIC_HIGH , MAGIC_LOW , 0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0  };
+        
+        Channel channel = getServerSideChannel(url);
+        UnsafeByteArrayInputStream input = new UnsafeByteArrayInputStream(header);
+        Object obj = codec.decode(channel, input);
+        Assert.assertEquals(TelnetCodec.NEED_MORE_INPUT, obj);
+        //如果telnet数据与request数据在同一个数据包中,不能因为telnet没有结尾字符而影响其他数据的接收.
+        Assert.assertEquals(2, input.position());
+    }
+    
+    @Test
+    public void test_Decode_Return_Response_Person() throws IOException{
+        //00000010-response/oneway/hearbeat=false/hessian |20-stats=ok|id=0|length=0
+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+        Person person = new Person();
+        byte[] request = getRequestBytes(person, header);
+        
+        Response obj = (Response)decode(request);
+        Assert.assertEquals(20, obj.getStatus());
+        Assert.assertEquals(person, obj.getResult());
+        System.out.println(obj);
+    }
+    
+    @Test //status输入有问题,序列化时读取信息出错.
+    public void test_Decode_Return_Response_Error() throws IOException{
+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 2, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+        String errorString = "encode request data error ";
+        byte[] request = getRequestBytes(errorString, header);
+        Response obj = (Response)decode(request);
+        Assert.assertEquals(90, obj.getStatus());
+        Assert.assertEquals(errorString, obj.getErrorMessage());
+    }
+    @Test
+    public void test_Decode_Return_Request_Event_Object() throws IOException{
+        //|10011111|20-stats=ok|id=0|length=0
+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) 0xff, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+        Person person = new Person();
+        byte[] request = getRequestBytes(person, header);
+        
+        Request obj = (Request)decode(request);
+        Assert.assertEquals(person, obj.getData());
+        Assert.assertEquals(true, obj.isTwoWay());
+        Assert.assertEquals(true, obj.isEvent());
+        Assert.assertEquals("2.0.0", obj.getVersion());
+        System.out.println(obj);
+    }

+    

+    @Test

+    public void test_Decode_Return_Request_Event_String() throws IOException{

+        //|10011111|20-stats=ok|id=0|length=0

+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) 0xff, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

+        String event = Request.READONLY_EVENT;

+        byte[] request = getRequestBytes(event, header);

+        

+        Request obj = (Request)decode(request);

+        Assert.assertEquals(event, obj.getData());

+        Assert.assertEquals(true, obj.isTwoWay());

+        Assert.assertEquals(true, obj.isEvent());

+        Assert.assertEquals("2.0.0", obj.getVersion());

+        System.out.println(obj);

+    }

+    

+    @Test

+    public void test_Decode_Return_Request_Heartbeat_Object() throws IOException{

+        //|10011111|20-stats=ok|id=0|length=0

+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) 0xff, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

+        byte[] request = getRequestBytes(null, header);

+        Request obj = (Request)decode(request);

+        Assert.assertEquals(null, obj.getData());

+        Assert.assertEquals(true, obj.isTwoWay());

+        Assert.assertEquals(true, obj.isHeartbeat());

+        Assert.assertEquals("2.0.0", obj.getVersion());

+        System.out.println(obj);

+    }

+    
+    @Test
+    public void test_Decode_Return_Request_Object() throws IOException{
+        //|10011111|20-stats=ok|id=0|length=0
+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) 0xdf, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+        Person person = new Person();
+        byte[] request = getRequestBytes(person, header);
+        
+        Request obj = (Request)decode(request);
+        Assert.assertEquals(person, obj.getData());
+        Assert.assertEquals(true, obj.isTwoWay());
+        Assert.assertEquals(false, obj.isHeartbeat());
+        Assert.assertEquals("2.0.0", obj.getVersion());
+        System.out.println(obj);
+    }
+    
+    @Test 
+    public void test_Decode_Error_Request_Object() throws IOException{
+       //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte)0xdf, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+        Person person = new Person();
+        byte[] request = getRequestBytes(person, header);
+        //bad object
+        byte[] badbytes = new byte[]{-1,-2,-3,-4,-3,-4,-3,-4,-3,-4,-3,-4};
+        System.arraycopy(badbytes, 0, request, 21, badbytes.length);
+        
+        Request obj = (Request)decode(request);
+        Assert.assertEquals(true, obj.isBroken());
+        Assert.assertEquals(true, obj.getData() instanceof Throwable);
+    }
+    
+    @Test
+    public void test_Header_Response_NoSerializationFlag() throws IOException{
+      //00000010-response/oneway/hearbeat=false/noset |20-stats=ok|id=0|length=0
+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+        Person person = new Person();
+        byte[] request = getRequestBytes(person, header);
+        
+        Response obj = (Response)decode(request);
+        Assert.assertEquals(20, obj.getStatus());
+        Assert.assertEquals(person, obj.getResult());
+        System.out.println(obj);
+    }
+    
+    @Test
+    public void test_Header_Response_Heartbeat() throws IOException{
+       //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
+        byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0x20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+        Person person = new Person();
+        byte[] request = getRequestBytes(person, header);
+        
+        Response obj = (Response)decode(request);
+        Assert.assertEquals(20, obj.getStatus());
+        Assert.assertEquals(person, obj.getResult());
+        System.out.println(obj);
+    }
+    
+    @Test 
+    public void test_Encode_Request() throws IOException{
+        UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+        Channel channel = getCliendSideChannel(url);
+        Request request = new Request();
+        Person person = new Person();
+        request.setData(person);
+        
+        codec.encode(channel, bos, request);
+        byte[] data = bos.toByteArray();        
+        bos.flush();
+        bos.close();
+        
+        //encode resault check need decode 
+        InputStream input = new UnsafeByteArrayInputStream(data);
+        Request obj = (Request)codec.decode(channel, input);
+        Assert.assertEquals(request.isBroken(), obj.isBroken());
+        Assert.assertEquals(request.isHeartbeat(), obj.isHeartbeat());
+        Assert.assertEquals(request.isTwoWay(), obj.isTwoWay());
+        Assert.assertEquals(person, obj.getData());
+    }
+    
+    @Test 
+    public void test_Encode_Response() throws IOException{
+        UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+        Channel channel = getCliendSideChannel(url);
+        Response response = new Response();
+        response.setHeartbeat(true);
+        response.setId(1001l);
+        response.setStatus((byte)20 );
+        response.setVersion("11");
+        Person person = new Person();
+        response.setResult(person);
+        
+        codec.encode(channel, bos, response);
+        byte[] data = bos.toByteArray();        
+        bos.flush();
+        bos.close();
+        
+        //encode resault check need decode 
+        InputStream input = new UnsafeByteArrayInputStream(data);
+        Response obj = (Response)codec.decode(channel, input);
+        
+        Assert.assertEquals(response.getId(), obj.getId());
+        Assert.assertEquals(response.getStatus(), obj.getStatus());
+        Assert.assertEquals(response.isHeartbeat(), obj.isHeartbeat());
+        Assert.assertEquals(person, obj.getResult());
+        // encode response verson ?? 
+//        Assert.assertEquals(response.getVersion(), obj.getVersion());
+        
+    }
+    
+    @Test 
+    public void test_Encode_Error_Response() throws IOException{
+        UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+        Channel channel = getCliendSideChannel(url);
+        Response response = new Response();
+        response.setHeartbeat(true);
+        response.setId(1001l);
+        response.setStatus((byte)10 );
+        response.setVersion("11");
+        String badString = "bad" ;
+        response.setErrorMessage(badString);
+        Person person = new Person();
+        response.setResult(person);
+        
+        codec.encode(channel, bos, response);
+        byte[] data = bos.toByteArray();        
+        bos.flush();
+        bos.close();
+        
+        //encode resault check need decode 
+        InputStream input = new UnsafeByteArrayInputStream(data);
+        Response obj = (Response)codec.decode(channel, input);
+        Assert.assertEquals(response.getId(), obj.getId());
+        Assert.assertEquals(response.getStatus(), obj.getStatus());
+        Assert.assertEquals(response.isHeartbeat(), obj.isHeartbeat());
+        Assert.assertEquals(badString, obj.getErrorMessage());
+        Assert.assertEquals(null, obj.getResult());
+//        Assert.assertEquals(response.getVersion(), obj.getVersion());
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/TelnetCodecTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/TelnetCodecTest.java
new file mode 100644
index 0000000..1f87b4b
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/TelnetCodecTest.java
@@ -0,0 +1,382 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.codec;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.HashMap;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class TelnetCodecTest {
+    protected Codec codec ;
+    byte[] UP = new byte[] {27, 91, 65};
+    byte[] DOWN = new byte[] {27, 91, 66};
+    /**
+     * @throws java.lang.Exception
+     */
+    @Before
+    public void setUp() throws Exception {
+        codec = new TelnetCodec();
+    }
+    
+    protected AbstractMockChannel getServerSideChannel(URL url){
+        url = url.addParameter(AbstractMockChannel.LOCAL_ADDRESS, url.getAddress())
+        .addParameter(AbstractMockChannel.REMOTE_ADDRESS, "127.0.0.1:12345");
+        AbstractMockChannel channel = new AbstractMockChannel(url);
+        return channel;
+    }
+    protected AbstractMockChannel getCliendSideChannel(URL url){
+        url = url.addParameter(AbstractMockChannel.LOCAL_ADDRESS, "127.0.0.1:12345")
+        .addParameter(AbstractMockChannel.REMOTE_ADDRESS, url.getAddress());
+        AbstractMockChannel channel = new AbstractMockChannel(url);
+        return channel;
+    }
+    
+    protected byte[] join(byte[] in1, byte[] in2){
+        byte[] ret = new byte[in1.length + in2.length];
+        System.arraycopy(in1, 0, ret, 0, in1.length);
+        System.arraycopy(in2, 0, ret, in1.length, in2.length);
+        return ret;
+    }
+    
+    protected byte[] objectToByte(Object obj){
+        byte[] bytes; 
+        if (obj instanceof String){
+            bytes = ((String)obj).getBytes();
+        } else if (obj instanceof byte[]){
+            bytes = (byte[]) obj;
+        } else {
+            try { 
+                //object to bytearray 
+                ByteArrayOutputStream bo = new ByteArrayOutputStream(); 
+                ObjectOutputStream oo = new ObjectOutputStream(bo); 
+                oo.writeObject(obj); 
+                bytes = bo.toByteArray(); 
+                bo.close(); 
+                oo.close();     
+            } 
+            catch(Exception e){ 
+                throw new RuntimeException(e);
+            } 
+        }
+        return(bytes); 
+    }
+    
+    protected Object byteToObject(byte[] objBytes) throws Exception {
+        if (objBytes == null || objBytes.length == 0) {
+            return null;
+        }
+        ByteArrayInputStream bi = new ByteArrayInputStream(objBytes);
+        ObjectInputStream oi = new ObjectInputStream(bi);
+        return oi.readObject();
+    }
+    
+    //======================================================
+    public static class Person implements Serializable{
+        private static final long serialVersionUID = 3362088148941547337L;
+        public String name ;
+        public String sex ;
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((name == null) ? 0 : name.hashCode());
+            result = prime * result + ((sex == null) ? 0 : sex.hashCode());
+            return result;
+        }
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            Person other = (Person) obj;
+            if (name == null) {
+                if (other.name != null)
+                    return false;
+            } else if (!name.equals(other.name))
+                return false;
+            if (sex == null) {
+                if (other.sex != null)
+                    return false;
+            } else if (!sex.equals(other.sex))
+                return false;
+            return true;
+        }
+        
+    }
+    
+    protected void testDecode_assertEquals(byte[] request,Object ret) throws IOException{
+        testDecode_assertEquals(request, ret, true);
+    }
+    
+    protected void testDecode_assertEquals(byte[] request,Object ret, boolean isServerside) throws IOException{
+        //init channel
+        Channel channel = isServerside? getServerSideChannel(url) : getCliendSideChannel(url);
+        //init request string
+        InputStream input = new UnsafeByteArrayInputStream(request);
+        
+        //decode
+        Object obj = codec.decode(channel, input);
+        Assert.assertEquals(ret, obj);
+    }
+    
+    
+    
+    protected void testEecode_assertEquals(Object request,byte[] ret, boolean isServerside) throws IOException{
+        //init channel
+        Channel channel = isServerside? getServerSideChannel(url) : getCliendSideChannel(url);
+        
+        UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+        
+        codec.encode(channel, bos, request);
+        bos.flush();
+        bos.close();
+        InputStream is = new ByteArrayInputStream(bos.toByteArray());
+        byte[] data = new byte[is.available()];
+        is.read(data);
+        
+        Assert.assertEquals(ret.length, data.length);
+        for(int i=0;i<ret.length;i++){
+            if (ret[i] != data[i]){
+                Assert.fail();
+            }
+        }
+    }
+    
+    protected void testDecode_assertEquals(Object request,Object ret) throws IOException{
+        testDecode_assertEquals(request, ret, null);
+    }
+    
+    private void testDecode_assertEquals(Object request,Object ret, Object channelReceive) throws IOException{
+        testDecode_assertEquals(null, request, ret, channelReceive);
+    }
+    
+    private void testDecode_assertEquals(AbstractMockChannel channel, Object request,Object expectret, Object channelReceive) throws IOException{
+        //init channel
+        if (channel == null){
+            channel = getServerSideChannel(url);
+        }
+        
+        byte[] buf = objectToByte(request);
+        InputStream input = new UnsafeByteArrayInputStream(buf);
+        
+        //decode
+        Object obj = codec.decode(channel, input);
+        Assert.assertEquals(expectret, obj);
+        Assert.assertEquals(channelReceive, channel.getReceivedMessage());
+    }
+    
+    private void testDecode_PersonWithEnterByte(byte[] enterbytes ,boolean isNeedmore) throws IOException{
+        //init channel
+        Channel channel = getServerSideChannel(url);
+        //init request string
+        Person request = new Person();
+        byte[] newbuf = join(objectToByte(request),enterbytes);
+        InputStream input = new UnsafeByteArrayInputStream(newbuf);
+        
+        //decode
+        Object obj = codec.decode(channel, input);
+        if (isNeedmore){
+            Assert.assertEquals(Codec.NEED_MORE_INPUT , obj);
+        }else {
+            Assert.assertTrue("return must string ", obj instanceof String);
+        }
+    }
+    
+    private void testDecode_WithExitByte(byte[] exitbytes ,boolean isChannelClose) throws IOException{
+        //init channel
+        Channel channel = getServerSideChannel(url);
+        InputStream input = new UnsafeByteArrayInputStream(exitbytes);
+        
+        //decode
+        codec.decode(channel, input);
+        Assert.assertEquals(isChannelClose, channel.isClosed());
+    }
+    
+    
+    //======================================================
+    URL url = URL.valueOf("dubbo://10.20.30.40:20880");
+    
+    @Test
+    public void testDecode_String_ClientSide() throws IOException{
+        testDecode_assertEquals("aaa".getBytes(), "aaa",false);
+    }
+    
+    @Test
+    public void testDecode_BlankMessage() throws IOException{
+        testDecode_assertEquals(new byte[]{}, Codec.NEED_MORE_INPUT);
+    }
+    
+    @Test
+    public void testDecode_String_NoEnter() throws IOException{
+        testDecode_assertEquals("aaa", Codec.NEED_MORE_INPUT);
+    }
+    
+    @Test
+    public void testDecode_String_WithEnter() throws IOException{
+        testDecode_assertEquals("aaa\n", "aaa");
+    }
+    @Test
+    public void testDecode_String_MiddleWithEnter() throws IOException{
+        testDecode_assertEquals("aaa\r\naaa", Codec.NEED_MORE_INPUT);
+    }
+    
+    @Test
+    public void testDecode_Person_ObjectOnly() throws IOException{
+        testDecode_assertEquals(new Person(), Codec.NEED_MORE_INPUT);
+    }
+    @Test
+    public void testDecode_Person_WithEnter() throws IOException{
+        testDecode_PersonWithEnterByte(new byte[] { '\r', '\n' } , false);//windows end
+        testDecode_PersonWithEnterByte(new byte[] { '\n', '\r' } , true); 
+        testDecode_PersonWithEnterByte(new byte[] { '\n' } , false); //linux end
+        testDecode_PersonWithEnterByte(new byte[] { '\r' } , true); 
+        testDecode_PersonWithEnterByte(new byte[] { '\r', 100 } , true);
+    }
+    @Test
+    public void testDecode_WithExitByte() throws IOException{
+        HashMap<byte[] , Boolean> exitbytes = new HashMap<byte[] , Boolean>();
+        exitbytes.put( new byte[] { 3 }, true ); /* Windows Ctrl+C */
+        exitbytes.put( new byte[] { 1, 3 }, false ); //must equal the bytes 
+        exitbytes.put( new byte[] { -1, -12, -1, -3, 6 }, true ); /* Linux Ctrl+C */
+        exitbytes.put( new byte[] {1,  -1, -12, -1, -3, 6 }, false ); //must equal the bytes 
+        exitbytes.put( new byte[] { -1, -19, -1, -3, 6 }, true );  /* Linux Pause */
+        
+        for (byte[] exit : exitbytes.keySet()){
+            testDecode_WithExitByte(exit ,exitbytes.get(exit));
+        }
+    }
+    @Test
+    public void testDecode_Backspace() throws IOException{
+        //32 8 先加空格在补退格.
+        testDecode_assertEquals(new byte[]{'\b'}, Codec.NEED_MORE_INPUT, new String(new byte[] {32, 8}));
+        
+        //测试中文
+        byte[] chineseBytes = "中".getBytes();
+        byte[] request = join(chineseBytes, new byte[]{'\b'});
+        testDecode_assertEquals(request, Codec.NEED_MORE_INPUT, new String(new byte[] {32, 32, 8, 8}));
+        //中文会带来此问题 (-数判断) 忽略此问题,退格键只有在真的telnet程序中才输入有意义.
+        testDecode_assertEquals(new byte[]{'a', 'x', -1, 'x', '\b'}, Codec.NEED_MORE_INPUT, new String(new byte[] {32, 32, 8, 8}));
+    }
+    
+    @Test(expected = IOException.class)
+    public void testDecode_Backspace_WithError() throws IOException{
+        url = url.addParameter(AbstractMockChannel.ERROR_WHEN_SEND, Boolean.TRUE.toString());
+        testDecode_Backspace();
+        url = url.removeParameter(AbstractMockChannel.ERROR_WHEN_SEND);
+    }
+    
+    @Test()
+    public void testDecode_History_UP() throws IOException{
+        //init channel
+        AbstractMockChannel channel = getServerSideChannel(url);
+        
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, null);
+        
+        String request1 = "aaa\n"; 
+        Object expected1 = "aaa";
+        //init history 
+        testDecode_assertEquals(channel, request1, expected1, null);
+        
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+    }
+    
+    @Test(expected = IOException.class)
+    public void testDecode_UPorDOWN_WithError() throws IOException{
+        url = url.addParameter(AbstractMockChannel.ERROR_WHEN_SEND, Boolean.TRUE.toString());
+        
+        //init channel
+        AbstractMockChannel channel = getServerSideChannel(url);
+        
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, null);
+        
+        String request1 = "aaa\n"; 
+        Object expected1 = "aaa";
+        //init history 
+        testDecode_assertEquals(channel, request1, expected1, null);
+        
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+        
+        url = url.removeParameter(AbstractMockChannel.ERROR_WHEN_SEND);
+    }
+    
+    /*@Test()
+    public void testDecode_History_UP_DOWN_MULTI() throws IOException{
+        AbstractMockChannel channel = getServerSideChannel(url);
+        
+        String request1 = "aaa\n"; 
+        Object expected1 = request1.replace("\n", "");
+        //init history 
+        testDecode_assertEquals(channel, request1, expected1, null);
+        
+        String request2 = "bbb\n"; 
+        Object expected2 = request2.replace("\n", "");
+        //init history 
+        testDecode_assertEquals(channel, request2, expected2, null);
+        
+        String request3 = "ccc\n"; 
+        Object expected3= request3.replace("\n", "");
+        //init history 
+        testDecode_assertEquals(channel, request3, expected3, null);
+        
+        byte[] UP = new byte[] {27, 91, 65};
+        byte[] DOWN = new byte[] {27, 91, 66};
+        //history[aaa,bbb,ccc]
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected3);
+        testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3);
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected2);
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+        testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected2);
+        testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3);
+        testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3);
+        testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3);
+        testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected2);
+    }*/
+    
+    
+    //=============================================================================================================================
+    @Test
+    public void testEncode_String_ClientSide() throws IOException{
+        testEecode_assertEquals("aaa", "aaa\r\n".getBytes(), false);
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java
new file mode 100644
index 0000000..aadd369
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.remoting.exchange.support.header;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.net.Authenticator;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartBeatTaskTest {
+
+    private MockChannel channel = new MockChannel();
+    private HeartBeatTask task;
+    
+    @Before
+    public void setup() throws Exception {
+        task = new HeartBeatTask( new HeartBeatTask.ChannelProvider() {
+
+            public Collection<Channel> getChannels() {
+                return Collections.<Channel>singletonList(channel);
+            }
+        }, 1000, 1000 * 3);
+    }
+    
+    @Test
+    public void testHeartBeat() throws Exception {
+        channel.setAttribute(
+                HeaderExchangeHandler.KEY_READ_TIMESTAMP, System.currentTimeMillis());
+        channel.setAttribute(
+                HeaderExchangeHandler.KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
+        Thread.sleep( 2000L );
+        task.run();
+        List<Object> objects = channel.getSentObjects();
+        Assert.assertTrue(objects.size() > 0);
+        Object obj = objects.get(0);
+        Assert.assertTrue(obj instanceof Request);
+        Request request = (Request)obj;
+        Assert.assertTrue(request.isHeartbeat());
+    }
+
+}
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/MockChannel.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/MockChannel.java
new file mode 100644
index 0000000..4f00c6b
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/MockChannel.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *    
+ *   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.dubbo.remoting.exchange.support.header;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MockChannel implements Channel {
+
+    private Map<String, Object> attributes = new HashMap<String, Object>();
+    
+    private volatile boolean closed = false;
+    private List<Object> sentObjects = new ArrayList<Object>();
+    
+    public InetSocketAddress getRemoteAddress() {
+        return null;
+    }
+
+    public boolean isConnected() {
+        return false;
+    }
+
+    public boolean hasAttribute(String key) {
+        return attributes.containsKey(key);
+    }
+
+    public Object getAttribute(String key) {
+        return attributes.get(key);
+    }
+
+    public void setAttribute(String key, Object value) {
+        attributes.put(key, value);
+    }
+
+    public void removeAttribute(String key) {
+        attributes.remove(key);
+    }
+
+    public URL getUrl() {
+        return null;
+    }
+
+    public ChannelHandler getChannelHandler() {
+        return null;
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        return null;
+    }
+
+    public void send(Object message) throws RemotingException {
+        sentObjects.add(message);
+    }
+
+    public void send(Object message, boolean sent) throws RemotingException {
+        sentObjects.add(message);
+    }
+
+    public void close() {
+        closed = true;
+    }
+
+    public void close(int timeout) {
+        closed = true;
+    }
+
+    public boolean isClosed() {
+        return closed;
+    }
+    
+    public List<Object> getSentObjects() {
+        return Collections.unmodifiableList(sentObjects);
+    }
+}
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.java
new file mode 100644
index 0000000..2b90b6c
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.java
@@ -0,0 +1,133 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.handler;
+
+import java.util.concurrent.ThreadPoolExecutor;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import junit.framework.Assert;

+

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.remoting.ExecutionException;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.Request;

+import com.alibaba.dubbo.remoting.exchange.Response;

+import com.alibaba.dubbo.remoting.transport.dispather.connection.ConnectionOrderedChannelHandler;

+
+
+
+public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest{
+
+    @Before
+    public void setUp() throws Exception {
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+    }
+    
+    @Test
+    public void test_Connect_Blocked() throws RemotingException{
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+        ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "connectionExecutor", 1);
+        Assert.assertEquals(1, executor.getMaximumPoolSize());
+        
+        int runs = 20;
+        int taskCount = runs * 2;
+        for(int i=0; i<runs;i++){
+            handler.connected(new MockedChannel());
+            handler.disconnected(new MockedChannel());
+            Assert.assertTrue(executor.getActiveCount() + " must <=1" ,executor.getActiveCount() <= 1);
+        }
+        //queue.size 
+        Assert.assertEquals(taskCount -1 , executor.getQueue().size());
+        
+        for( int i=0;i<taskCount; i++){
+            if (executor.getCompletedTaskCount() < taskCount){
+                sleep(100);
+            }
+        }
+        Assert.assertEquals(taskCount, executor.getCompletedTaskCount());        
+    }
+    
+    @Test //biz error 不抛出到线程异常上来.
+    public void test_Connect_Biz_Error() throws RemotingException{
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+        handler.connected(new MockedChannel());
+    }
+    @Test //biz error 不抛出到线程异常上来.
+    public void test_Disconnect_Biz_Error() throws RemotingException{
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+        handler.disconnected(new MockedChannel());
+    }
+    
+    @Test(expected = ExecutionException.class)
+    public void test_Connect_Execute_Error() throws RemotingException{
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+        ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "connectionExecutor", 1);
+        executor.shutdown();
+        handler.connected(new MockedChannel());
+    }
+    @Test(expected = ExecutionException.class)
+    public void test_Disconnect_Execute_Error() throws RemotingException{
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+        ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "connectionExecutor", 1);
+        executor.shutdown();
+        handler.disconnected(new MockedChannel());
+    }
+    //throw  ChannelEventRunnable.runtimeExeception(int logger) not in execute exception
+    @Test//(expected = RemotingException.class)
+    public void test_MessageReceived_Biz_Error() throws RemotingException{
+        handler.received(new MockedChannel(),"");
+    }
+    //throw  ChannelEventRunnable.runtimeExeception(int logger) not in execute exception
+    @Test
+    public void test_Caught_Biz_Error() throws RemotingException{
+        handler.caught(new MockedChannel(), new BizException());
+    }
+    @Test(expected = ExecutionException.class)
+    public void test_Received_InvokeInExecuter() throws RemotingException{
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+        ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "SHARED_EXECUTOR", 1);
+        executor.shutdown();
+        executor = (ThreadPoolExecutor)getField(handler, "executor", 1);
+        executor.shutdown();
+        handler.received(new MockedChannel(), "");
+    }

+    

+    /**

+     * 事件不通过线程池,直接在IO上执行

+     */

+    @SuppressWarnings("deprecation")

+    @Test

+    public void test_Received_Event_invoke_direct() throws RemotingException{

+        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);

+        ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "SHARED_EXECUTOR", 1);

+        executor.shutdown();

+        executor = (ThreadPoolExecutor)getField(handler, "executor", 1);

+        executor.shutdown();

+        Request req = new Request();

+        req.setHeartbeat(true);

+        final AtomicInteger count = new AtomicInteger(0);

+        handler.received(new MockedChannel(){

+            @Override

+            public void send(Object message) throws RemotingException {

+                Assert.assertEquals("response.heartbeat", true, ((Response)message).isHeartbeat());

+                count.incrementAndGet();

+            }

+        }, req);

+        Assert.assertEquals("channel.send must be invoke", 1, count.get());

+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/HeaderExchangeHandlerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/HeaderExchangeHandlerTest.java
new file mode 100644
index 0000000..e61f282
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/HeaderExchangeHandlerTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.remoting.handler;
+
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler;
+
+//TODO response test
+public class HeaderExchangeHandlerTest {
+    
+    @Test
+    public void test_received_request_oneway() throws RemotingException{
+        final Channel mchannel = new MockedChannel();
+        
+        final Person requestdata = new Person("charles");
+        Request request = new Request();
+        request.setTwoWay(false);
+        request.setData(requestdata);
+        
+        ExchangeHandler exhandler = new MockedExchangeHandler(){
+            public void received(Channel channel, Object message) throws RemotingException {
+                Assert.assertEquals(requestdata, message);
+            }
+        };
+        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(exhandler);
+        hexhandler.received(mchannel, request);
+    }
+    
+    @Test
+    public void test_received_request_twoway() throws RemotingException{
+        final Person requestdata = new Person("charles");
+        final Request request = new Request();
+        request.setTwoWay(true);
+        request.setData(requestdata);
+        
+        final AtomicInteger count = new AtomicInteger(0);
+        final Channel mchannel = new MockedChannel(){
+            @Override
+            public void send(Object message) throws RemotingException {
+                Response res = (Response)message;
+                Assert.assertEquals(request.getId(), res.getId());
+                Assert.assertEquals(request.getVersion(), res.getVersion());
+                Assert.assertEquals(Response.OK, res.getStatus());
+                Assert.assertEquals(requestdata, res.getResult());
+                Assert.assertEquals(null, res.getErrorMessage());
+                count.incrementAndGet();
+            }
+        };
+        ExchangeHandler exhandler = new MockedExchangeHandler(){
+            @Override
+            public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
+                return request;
+            }
+            public void received(Channel channel, Object message) throws RemotingException {
+                Assert.fail();
+            }
+        };
+        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(exhandler);
+        hexhandler.received(mchannel, request);
+        Assert.assertEquals(1, count.get());
+    }
+    
+    @Test(expected = IllegalArgumentException.class)
+    public void test_received_request_twoway_error_nullhandler() throws RemotingException{
+        new HeaderExchangeHandler(null);
+    }
+    @Test
+    public void test_received_request_twoway_error_reply() throws RemotingException{
+        final Person requestdata = new Person("charles");
+        final Request request = new Request();
+        request.setTwoWay(true);
+        request.setData(requestdata);
+        
+        final AtomicInteger count = new AtomicInteger(0);
+        final Channel mchannel = new MockedChannel(){
+            @Override
+            public void send(Object message) throws RemotingException {
+                Response res = (Response)message;
+                Assert.assertEquals(request.getId(), res.getId());
+                Assert.assertEquals(request.getVersion(), res.getVersion());
+                Assert.assertEquals(Response.SERVICE_ERROR, res.getStatus());
+                Assert.assertNull(res.getResult());
+                Assert.assertTrue(res.getErrorMessage().contains(BizException.class.getName()));
+                count.incrementAndGet();
+            }
+        };
+        ExchangeHandler exhandler = new MockedExchangeHandler(){
+            @Override
+            public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
+                throw new BizException();
+            }
+        };
+        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(exhandler);
+        hexhandler.received(mchannel, request);
+        Assert.assertEquals(1, count.get());
+    }
+    
+    @Test
+    public void test_received_request_twoway_error_reqeustBroken() throws RemotingException{
+        final Request request = new Request();
+        request.setTwoWay(true);
+        request.setData(new BizException());
+        request.setBroken(true);
+        
+        final AtomicInteger count = new AtomicInteger(0);
+        final Channel mchannel = new MockedChannel(){
+            @Override
+            public void send(Object message) throws RemotingException {
+                Response res = (Response)message;
+                Assert.assertEquals(request.getId(), res.getId());
+                Assert.assertEquals(request.getVersion(), res.getVersion());
+                Assert.assertEquals(Response.BAD_REQUEST, res.getStatus());
+                Assert.assertNull(res.getResult());
+                Assert.assertTrue(res.getErrorMessage().contains(BizException.class.getName()));
+                count.incrementAndGet();
+            }
+        };
+        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(new MockedExchangeHandler());
+        hexhandler.received(mchannel, request);
+        Assert.assertEquals(1, count.get());
+    }
+    
+    @Test
+    public void test_received_request_event_heartbeat() throws RemotingException{
+        final Request request = new Request();
+        request.setTwoWay(true);
+        request.setEvent(Request.HEARTBEAT_EVENT);
+        
+        final AtomicInteger count = new AtomicInteger(0);
+        final Channel mchannel = new MockedChannel(){
+            @Override
+            public void send(Object message) throws RemotingException {
+                Response res = (Response)message;
+                Assert.assertEquals(request.getId(), res.getId());
+                Assert.assertEquals(request.getVersion(), res.getVersion());
+                Assert.assertTrue(res.isHeartbeat());
+                Assert.assertNull(res.getResult());
+                count.incrementAndGet();
+            }
+        };
+        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(new MockedExchangeHandler());
+        hexhandler.received(mchannel, request);
+        Assert.assertEquals(1, count.get());
+    }
+    
+    @Test
+    public void test_received_request_event_readonly() throws RemotingException{
+        final Request request = new Request();
+        request.setTwoWay(true);
+        request.setEvent(Request.READONLY_EVENT);
+        
+        final Channel mchannel = new MockedChannel();
+        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(new MockedExchangeHandler());
+        hexhandler.received(mchannel, request);
+        Assert.assertTrue(mchannel.hasAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY));
+    }
+    
+    @Test
+    public void test_received_request_event_other_discard() throws RemotingException{
+        final Request request = new Request();
+        request.setTwoWay(true);
+        request.setEvent("my event");
+        
+        final Channel mchannel = new MockedChannel(){
+            @Override
+            public void send(Object message) throws RemotingException {
+                Assert.fail();
+            }
+        };
+        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(new MockedExchangeHandler(){
+
+            @Override
+            public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
+                Assert.fail();
+                throw new RemotingException(channel,"");
+            }
+
+            @Override
+            public void received(Channel channel, Object message) throws RemotingException {
+                Assert.fail();
+                throw new RemotingException(channel,"");
+            }
+        });
+        hexhandler.received(mchannel, request);
+    }
+
+    private class BizException extends RuntimeException{
+        private static final long serialVersionUID = 1L;
+    }
+    
+    private class MockedExchangeHandler extends MockedChannelHandler implements ExchangeHandler{
+
+        public String telnet(Channel channel, String message) throws RemotingException {
+            throw new UnsupportedOperationException();
+        }
+
+        public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
+            throw new UnsupportedOperationException();
+        }
+    }
+    
+    private class Person {
+        private String name;
+        public Person(String name) {
+            super();
+            this.name = name;
+        }
+        @Override
+        public String toString() {
+            return "Person [name=" + name + "]";
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannel.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannel.java
new file mode 100644
index 0000000..512d575
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannel.java
@@ -0,0 +1,98 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.handler;
+
+import java.net.InetSocketAddress;

+import java.util.HashMap;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+
+/**
+ * @author chao.liuc
+ *
+ */
+public class MockedChannel implements Channel {
+    private boolean isClosed ; 
+    private URL url; 
+    private ChannelHandler handler ;

+    private Map <String,Object> map = new HashMap<String, Object>(); 
+    
+    public MockedChannel() {
+        super();
+    }
+
+
+    public URL getUrl() {
+        return url;
+    }
+
+    public ChannelHandler getChannelHandler() {
+        
+        return this.handler;
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        
+        return null;
+    }
+
+    public void send(Object message) throws RemotingException {
+    }
+
+    public void send(Object message, boolean sent) throws RemotingException {
+        this.send(message);
+    }
+
+    public void close() {
+        isClosed = true;
+    }
+
+    public void close(int timeout) {
+        this.close();
+    }
+
+    public boolean isClosed() {
+        return isClosed;
+    }
+
+    public InetSocketAddress getRemoteAddress() {
+        return null;
+    }
+
+    public boolean isConnected() {
+        return false;
+    }
+
+    public boolean hasAttribute(String key) {
+        return map.containsKey(key);
+    }
+
+    public Object getAttribute(String key) {
+        return map.get(key);
+    }
+
+    public void setAttribute(String key, Object value) {
+        map.put(key, value);
+    }
+
+    public void removeAttribute(String key) {
+        map.remove(key);
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannelHandler.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannelHandler.java
new file mode 100644
index 0000000..08744da
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannelHandler.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.handler;
+
+import java.util.Collections;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class MockedChannelHandler implements ChannelHandler {
+//    ConcurrentMap<String, Channel> channels = new ConcurrentHashMap<String, Channel>();
+    ConcurrentHashSet<Channel> channels = new ConcurrentHashSet<Channel>();
+
+    public void connected(Channel channel) throws RemotingException {
+        channels.add(channel);
+    }
+
+    public void disconnected(Channel channel) throws RemotingException {
+        channels.remove(channel);
+    }
+
+    public void sent(Channel channel, Object message) throws RemotingException {
+        channel.send(message);
+    }
+
+    public void received(Channel channel, Object message) throws RemotingException {
+        //echo 
+        channel.send(message);
+    }
+
+    public void caught(Channel channel, Throwable exception) throws RemotingException {
+        throw new RemotingException(channel, exception);
+        
+    }
+    public Set<Channel> getChannels(){
+        return Collections.unmodifiableSet(channels);
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/WrappedChannelHandlerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/WrappedChannelHandlerTest.java
new file mode 100644
index 0000000..dc981a4
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/WrappedChannelHandlerTest.java
@@ -0,0 +1,140 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.remoting.handler;
+
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Field;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;

+
+public class WrappedChannelHandlerTest {
+    WrappedChannelHandler handler ;
+    URL url = URL.valueOf("test://10.20.30.40:1234");
+
+    @Before
+    public void setUp() throws Exception {
+        handler = new WrappedChannelHandler(new BizChannelHander(true), url);
+    }
+    
+    @Test
+    public void test_Execute_Error() throws RemotingException{
+        
+    }
+    
+    
+    protected Object getField(Object obj, String fieldName, int parentdepth){
+        try{
+            Class<?> clazz = obj.getClass();
+            Field field = null;
+            for(int i=0;i <= parentdepth && field == null ;i++){
+                Field[] fields = clazz.getDeclaredFields();
+                for(Field f : fields){
+                    if (f.getName().equals(fieldName)){
+                        field = f;
+                        break;
+                    }
+                }
+                clazz = clazz.getSuperclass();
+            }
+            if (field != null){
+                field.setAccessible(true);
+                return field.get(obj);
+            }else {
+                throw new NoSuchFieldException();
+            }
+        }catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+    protected void sleep(int ms){
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+    
+    class BizChannelHander extends MockedChannelHandler{
+        private boolean invokeWithBizError;
+        
+        public BizChannelHander(boolean invokeWithBizError) {
+            super();
+            this.invokeWithBizError = invokeWithBizError;
+        }
+        public BizChannelHander() {
+            super();
+        }
+
+        @Override
+        public void connected(Channel channel) throws RemotingException {
+            if (invokeWithBizError){
+                throw new RemotingException(channel, "test connect biz error");
+            }
+            sleep(20);
+        }
+
+        @Override
+        public void disconnected(Channel channel) throws RemotingException {
+            if (invokeWithBizError){
+                throw new RemotingException(channel, "test disconnect biz error");
+            }
+            sleep(20);
+        }
+        @Override
+        public void received(Channel channel, Object message) throws RemotingException {
+            if (invokeWithBizError){
+                throw new RemotingException(channel, "test received biz error");
+            }
+            sleep(20);
+        }
+    };
+    
+    class BizException extends RuntimeException{
+        private static final long serialVersionUID = -7541893754900723624L;
+    }
+    
+    @Test(expected = RemotingException.class)
+    public void test_Connect_Biz_Error() throws RemotingException{
+        handler.connected(new MockedChannel());
+    }
+    @Test(expected = RemotingException.class)
+    public void test_Disconnect_Biz_Error() throws RemotingException{
+        handler.disconnected(new MockedChannel());
+    }
+    @Test(expected = RemotingException.class)
+    public void test_MessageReceived_Biz_Error() throws RemotingException{
+        handler.received(new MockedChannel(),"");
+    }
+    @Test
+    public void test_Caught_Biz_Error() throws RemotingException{
+        try{
+            handler.caught(new MockedChannel(), new BizException());
+            fail();
+        }catch (Exception e) {
+            Assert.assertEquals(BizException.class, e.getCause().getClass());
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/resources/log4j.xml b/dubbo-remoting/src/test/resources/log4j.xml
new file mode 100644
index 0000000..4a01abe
--- /dev/null
+++ b/dubbo-remoting/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+	<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />
+		</layout>
+	</appender>
+	<root>
+		<level value="WARN" />
+		<appender-ref ref="CONSOLE" />
+	</root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-rpc-default/pom.xml b/dubbo-rpc-default/pom.xml
new file mode 100644
index 0000000..85c9fd4
--- /dev/null
+++ b/dubbo-rpc-default/pom.xml
@@ -0,0 +1,65 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-rpc-default</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Default RPC Module</name>
+	<description>The default rpc module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-rpc</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-container</artifactId>

+			<version>${project.parent.version}</version>

+			<exclusions>

+				<exclusion>

+					<groupId>org.mortbay.jetty</groupId>

+					<artifactId>jetty</artifactId>

+				</exclusion>

+			</exclusions>

+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting-netty</artifactId>
+			<version>${project.parent.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.mina</groupId>
+			<artifactId>mina-core</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
new file mode 100644
index 0000000..4b68956
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
@@ -0,0 +1,288 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import java.io.IOException;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.Set;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.bytecode.Wrapper;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+
+/**
+ * callback 服务帮助类.
+ * @author chao.liuc
+ *
+ */
+class CallbackServiceCodec {
+    private static final Logger     logger             = LoggerFactory.getLogger(CallbackServiceCodec.class);
+    
+    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    private static final DubboProtocol protocol = DubboProtocol.getDubboProtocol();
+    private static final byte CALLBACK_NONE = 0x0;
+    private static final byte CALLBACK_CREATE = 0x1;
+    private static final byte CALLBACK_DESTROY = 0x2;
+    private static final String INV_ATT_CALLBACK_KEY  = "sys_callback_arg-";

+    
+    private static byte isCallBack(URL url, String methodName ,int argIndex){
+        //参数callback的规则是 方法名称.参数index(0开始).callback
+        byte isCallback = CALLBACK_NONE;
+        if (url != null ) {
+            String callback = url.getParameter(methodName+"."+argIndex+".callback");
+            if(callback !=  null) {
+                if (callback.equalsIgnoreCase("true")) { 
+                    isCallback = CALLBACK_CREATE;
+                }else if(callback.equalsIgnoreCase("false")){
+                    isCallback = CALLBACK_DESTROY;
+                }
+            }
+        }
+        return isCallback;
+    }
+    
+    /**
+     * client 端export callback service

+     * @param channel

+     * @param clazz

+     * @param inst

+     * @param export

+     * @param out
+     * @throws IOException
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static String exportOrunexportCallbackService(Channel channel, URL url, Class clazz, Object inst, Boolean export) throws IOException{
+        int instid = System.identityHashCode(inst);
+        
+        Map<String,String> params = new HashMap<String,String>(3);
+        //不需要在重新new client
+        params.put(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
+        //标识callback 变于排查问题
+        params.put(Constants.IS_CALLBACK_SERVICE, Boolean.TRUE.toString());
+        String group = url.getParameter(Constants.GROUP_KEY);
+        if (group != null && group.length() > 0){
+            params.put(Constants.GROUP_KEY,group);
+        }
+        //增加方法,变于方法检查,自动降级(见dubbo protocol)
+        params.put(Constants.METHODS_KEY, StringUtils.join(Wrapper.getWrapper(clazz).getDeclaredMethodNames(), ","));
+        
+        Map<String, String> tmpmap = new HashMap<String, String>(url.getParameters());
+        tmpmap.putAll(params);
+        tmpmap.remove(Constants.VERSION_KEY);//callback不需要区分version
+        URL exporturl = new URL(DubboProtocol.NAME, channel.getLocalAddress().getAddress().getHostAddress(), channel.getLocalAddress().getPort(), clazz.getName()+"."+instid, tmpmap);
+        
+        //同一个jvm不需要对不同的channel产生多个exporter cache key不会碰撞 
+        String cacheKey = getClientSideCallbackServiceCacheKey(instid);
+        String countkey = getClientSideCountKey(clazz.getName());
+        if(export){
+            //同一个channel 可以有多个callback instance. 不同的instance不重新export
+            if( ! channel.hasAttribute(cacheKey)){
+                if (!isInstancesOverLimit(channel, url, clazz.getName(), instid, false)) {
+                    Invoker<?> invoker = proxyFactory.getInvoker(inst, clazz, exporturl);
+                    //资源销毁?
+                    Exporter<?> exporter = protocol.export(invoker);
+                    //这个用来记录instid是否发布过服务
+                    channel.setAttribute(cacheKey, exporter);
+                    logger.info("export a callback service :"+exporturl +", on "+channel + ", url is: " + url);
+                    increaseInstanceCount(channel, countkey);
+                }
+            }
+        }else {
+            if(channel.hasAttribute(cacheKey)){
+                Exporter<?> exporter = (Exporter<?>) channel.getAttribute(cacheKey);
+                exporter.unexport();
+                channel.removeAttribute(cacheKey);
+                decreaseInstanceCount(channel, countkey);
+            }
+        }     
+        return String.valueOf(instid);
+    }
+    
+    /**
+     * server端 应用一个callbackservice

+     * @param url 
+     */
+    @SuppressWarnings("unchecked")
+    private static Object referOrdestroyCallbackService(Channel channel, URL url, Class<?> clazz ,Invocation inv ,int instid, boolean isRefer){
+        Object proxy = null;
+        String invokerCacheKey = getServerSideCallbackInvokerCacheKey(channel, clazz.getName(), instid);
+        String proxyCacheKey = getServerSideCallbackServiceCacheKey(channel, clazz.getName(), instid);
+        proxy = channel.getAttribute(proxyCacheKey) ;
+        String countkey = getServerSideCountKey(channel, clazz.getName());
+        if (isRefer){
+            if( proxy == null ){
+                if (!isInstancesOverLimit(channel, url, clazz.getName(), instid, true)){

+                    url = url.setPath(clazz.getName());
+                    @SuppressWarnings("rawtypes")

+                    Invoker<?> invoker = new ChannelWrappedInvoker(clazz, channel, url, String.valueOf(instid));
+                    proxy = proxyFactory.getProxy(invoker);
+                    channel.setAttribute(proxyCacheKey, proxy);
+                    channel.setAttribute(invokerCacheKey, invoker);
+                    increaseInstanceCount(channel, countkey);
+                    
+                    //convert error fail fast .
+                    //ignore concurrent problem. 
+                    Set<Invoker<?>> callbackInvokers = (Set<Invoker<?>>)channel.getAttribute(Constants.CHANNEL_CALLBACK_KEY);
+                    if (callbackInvokers == null){
+                        callbackInvokers = new ConcurrentHashSet<Invoker<?>>(1);
+                        callbackInvokers.add(invoker);
+                        channel.setAttribute(Constants.CHANNEL_CALLBACK_KEY, callbackInvokers);
+                    }
+                    logger.info ("method "+inv.getMethodName()+" include a callback service :"+invoker.getUrl() +", a proxy :"+invoker +" has been created.") ;
+                }
+            } 
+        } else {
+            if(proxy != null){
+                Invoker<?> invoker = (Invoker<?>)channel.getAttribute(invokerCacheKey);
+                try{
+                    Set<Invoker<?>> callbackInvokers = (Set<Invoker<?>>)channel.getAttribute(Constants.CHANNEL_CALLBACK_KEY);
+                    if (callbackInvokers != null ) {
+                        callbackInvokers.remove(invoker);
+                    }
+                    invoker.destroy();
+                }catch (Exception e) {
+                    logger.error(e.getMessage(), e);
+                }
+                //取消refer 直接在map中去除,
+                channel.removeAttribute(proxyCacheKey);
+                channel.removeAttribute(invokerCacheKey);
+                decreaseInstanceCount(channel,countkey);
+            }
+        } 
+        return proxy;
+    }
+    
+    private static String getClientSideCallbackServiceCacheKey(int instid){
+        return Constants.CALLBACK_SERVICE_KEY+"."+instid;
+    }
+    private static String getServerSideCallbackServiceCacheKey(Channel channel, String interfaceClass, int instid){
+        return Constants.CALLBACK_SERVICE_PROXY_KEY+"."+System.identityHashCode(channel)+"."+ interfaceClass +"."+instid;
+    }
+    private static String getServerSideCallbackInvokerCacheKey(Channel channel, String interfaceClass, int instid){
+        return getServerSideCallbackServiceCacheKey(channel, interfaceClass, instid) + "." + "invoker";
+    }
+    
+    private static String getClientSideCountKey(String interfaceClass){
+        return Constants.CALLBACK_SERVICE_KEY+"."+interfaceClass+".COUNT";
+    }
+    private static String getServerSideCountKey(Channel channel, String interfaceClass){
+        return Constants.CALLBACK_SERVICE_PROXY_KEY+"."+System.identityHashCode(channel)+"."+interfaceClass+".COUNT";
+    }
+    private static boolean isInstancesOverLimit(Channel channel, URL url ,String interfaceClass, int instid, boolean isServer){
+        Integer count = (Integer)channel.getAttribute(isServer ? getServerSideCountKey(channel,interfaceClass) : getClientSideCountKey(interfaceClass));
+        int limit = url.getParameter(Constants.CALLBACK_INSTANCES_LIMIT_KEY, Constants.DEFAULT_CALLBACK_INSTANCES);
+        if (count != null && count >= limit){
+            //client side error
+            throw new IllegalStateException("interface " + interfaceClass +" `s callback instances num exceed providers limit :"+ limit 
+                    +" ,current num: "+(count+1)+". The new callback service will not work !!! you can cancle the callback service which exported before. channel :"+ channel);
+        }else {
+            return false;
+        }
+    }
+    private static void increaseInstanceCount(Channel channel, String countkey){
+        try{
+            //ignore cuncurrent problem? 
+            Integer count = (Integer)channel.getAttribute(countkey);
+            if (count == null ){
+                count = 1;
+            }else {
+                count ++ ;
+            }
+            channel.setAttribute(countkey, count);
+        }catch (Exception e) {
+            logger.error(e.getMessage(), e);
+        }
+    }
+    private static void decreaseInstanceCount(Channel channel, String countkey){
+        try{
+            Integer count = (Integer)channel.getAttribute(countkey);
+            if (count == null || count <= 0){
+                return;
+            }else {
+                count -- ;
+            }
+            channel.setAttribute(countkey, count);
+        }catch (Exception e) {
+            logger.error(e.getMessage(), e);
+        }
+    }
+    
+    public static Object encodeInvocationArgument(Channel channel, RpcInvocation inv, int paraIndex) throws IOException{

+        //encode时可直接获取url

+        URL url = inv.getInvoker() == null ? null : inv.getInvoker().getUrl();
+        byte callbackstatus = isCallBack(url, inv.getMethodName(), paraIndex);
+        Object[] args = inv.getArguments();
+        Class<?>[] pts = inv.getParameterTypes();
+        switch (callbackstatus) {
+            case CallbackServiceCodec.CALLBACK_NONE:
+                return args[paraIndex];
+            case CallbackServiceCodec.CALLBACK_CREATE:
+                inv.setAttachment(INV_ATT_CALLBACK_KEY + paraIndex , exportOrunexportCallbackService(channel, url, pts[paraIndex], args[paraIndex], true));
+                return null;
+            case CallbackServiceCodec.CALLBACK_DESTROY:
+                inv.setAttachment(INV_ATT_CALLBACK_KEY + paraIndex, exportOrunexportCallbackService(channel, url, pts[paraIndex], args[paraIndex], false));
+                return null;
+            default:
+                return args[paraIndex];
+        }
+    }
+    public static Object decodeInvocationArgument(Channel channel, RpcInvocation inv, Class<?>[] pts, int paraIndex, Object inObject) throws IOException{
+        //如果是callback,则创建proxy到客户端,方法的执行可通过channel调用到client端的callback接口

+        //decode时需要根据channel及env获取url

+        URL url = null ;

+        try {

+            url = DubboProtocol.getDubboProtocol().getInvoker(channel, inv).getUrl();

+        } catch (RemotingException e) {

+            if (logger.isInfoEnabled()) {

+                logger.info(e.getMessage(), e);

+            }

+            return inObject;

+        }
+        byte callbackstatus = isCallBack(url, inv.getMethodName(), paraIndex);
+        switch (callbackstatus) {
+            case CallbackServiceCodec.CALLBACK_NONE:
+                return inObject;
+            case CallbackServiceCodec.CALLBACK_CREATE:
+                try{
+                    return referOrdestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), true);
+                }catch (Exception e) {
+                    logger.error(e.getMessage(), e);
+                    throw new IOException(StringUtils.toString(e));
+                }
+            case CallbackServiceCodec.CALLBACK_DESTROY:
+                try{
+                    return referOrdestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), false);
+                }catch (Exception e) {
+                    throw new IOException(StringUtils.toString(e));
+                }
+            default:
+                return inObject ;
+        }
+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java
new file mode 100644
index 0000000..e87d0be
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java
@@ -0,0 +1,168 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.TimeoutException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient;
+import com.alibaba.dubbo.remoting.transport.ClientDelegate;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+
+/**
+ * 基于已有channel的invoker. 
+ * 
+ * @author chao.liuc
+ */
+class ChannelWrappedInvoker<T> extends AbstractInvoker<T> {
+
+    private final Channel channel;
+    private final String serviceKey ; 
+
+    public ChannelWrappedInvoker(Class<T> serviceType, Channel channel, URL url, String serviceKey) {
+
+        super(serviceType, url, new String[] { Constants.GROUP_KEY,
+                Constants.TOKEN_KEY, Constants.TIMEOUT_KEY });
+        this.channel = channel;
+        this.serviceKey = serviceKey;
+    }
+
+    @Override
+    protected Result doInvoke(Invocation invocation) throws Throwable {
+        RpcInvocation inv = new RpcInvocation(invocation);
+        //拿不到client端export 的service path.约定为interface的名称.
+        inv.setAttachment(Constants.PATH_KEY, getInterface().getName());
+        inv.setAttachment(Constants.CALLBACK_SERVICE_KEY, serviceKey);
+
+        ExchangeClient currentClient = new HeaderExchangeClient(new ChannelWrapper(this.channel));
+
+        try {
+            if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) { // 不可靠异步
+                currentClient.send(inv,getUrl().getMethodParameter(invocation.getMethodName(), Constants.SENT_KEY, false));
+                return new RpcResult();
+            }
+            int timeout = getUrl().getMethodParameter(invocation.getMethodName(),
+                    Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+            if (timeout > 0) {
+                return (Result) currentClient.request(inv, timeout).get();
+            } else {
+                return (Result) currentClient.request(inv).get();
+            }
+        } catch (RpcException e) {
+            throw e;
+        } catch (TimeoutException e) {
+            throw new RpcException(RpcException.TIMEOUT_EXCEPTION, e.getMessage(), e);
+        } catch (RemotingException e) {
+            throw new RpcException(RpcException.NETWORK_EXCEPTION, e.getMessage(), e);
+        } catch (Throwable e) { // here is non-biz exception, wrap it.
+            throw new RpcException(e.getMessage(), e);
+        }
+    }
+
+    public static class ChannelWrapper extends ClientDelegate {
+
+        private final Channel channel;
+        private final URL     url;
+
+        public ChannelWrapper(Channel channel) {
+            this.channel = channel;
+            this.url = channel.getUrl().addParameter("codec", DubboCodec.NAME);
+        }
+
+        public URL getUrl() {
+            return url;
+        }
+
+        public ChannelHandler getChannelHandler() {
+            return channel.getChannelHandler();
+        }
+
+        public InetSocketAddress getLocalAddress() {
+            return channel.getLocalAddress();
+        }
+
+        public void close() {
+            channel.close();
+        }
+
+        public boolean isClosed() {
+            return channel == null ? true : channel.isClosed();
+        }
+
+        public void reset(URL url) {
+            throw new RpcException("ChannelInvoker can not reset.");
+        }
+
+        public InetSocketAddress getRemoteAddress() {
+            return channel.getLocalAddress();
+        }
+
+        public boolean isConnected() {
+            return channel == null ? false : channel.isConnected();
+        }
+
+        public boolean hasAttribute(String key) {
+            return channel.hasAttribute(key);
+        }
+
+        public Object getAttribute(String key) {
+            return channel.getAttribute(key);
+        }
+
+        public void setAttribute(String key, Object value) {
+            channel.setAttribute(key, value);
+        }
+
+        public void removeAttribute(String key) {
+            channel.removeAttribute(key);
+        }
+
+        public void reconnect() throws RemotingException {
+
+        }
+
+        public void send(Object message) throws RemotingException {
+            channel.send(message);
+        }
+
+        public void send(Object message, boolean sent) throws RemotingException {
+            channel.send(message, sent);
+        }
+
+    }
+
+    public void destroy() {
+        //channel资源的清空由channel创建者清除.
+//        super.destroy();
+//        try {
+//            channel.close();
+//        } catch (Throwable t) {
+//            logger.warn(t.getMessage(), t);
+//        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java
new file mode 100644
index 0000000..dcb67be
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java
@@ -0,0 +1,186 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import static com.alibaba.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.decodeInvocationArgument;

+import static com.alibaba.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.encodeInvocationArgument;

+

+import java.io.IOException;

+import java.lang.reflect.Type;

+import java.util.HashMap;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.serialize.ObjectInput;

+import com.alibaba.dubbo.common.serialize.ObjectOutput;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.Codec;

+import com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.support.RpcUtils;

+
+/**
+ * Dubbo codec.
+ * 
+ * @author qianlei
+ * @author chao.liuc
+ */
+public class DubboCodec extends ExchangeCodec implements Codec {

+

+    public static final String      NAME                    = "dubbo";
+
+    private static final String     DUBBO_VERSION           = Version.getVersion(DubboCodec.class, Version.getVersion());
+
+    private static final byte       RESPONSE_WITH_EXCEPTION = 0;
+
+    private static final byte       RESPONSE_VALUE          = 1;
+
+    private static final byte       RESPONSE_NULL_VALUE     = 2;
+
+    private static final Object[]   EMPTY_OBJECT_ARRAY      = new Object[0];
+
+    private static final Class<?>[] EMPTY_CLASS_ARRAY       = new Class<?>[0];
+    
+   
+    @Override
+    protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {
+        RpcInvocation inv = (RpcInvocation) data;
+
+        out.writeUTF(inv.getAttachment(Constants.DUBBO_VERSION_KEY, DUBBO_VERSION));
+        out.writeUTF(inv.getAttachment(Constants.PATH_KEY));
+        out.writeUTF(inv.getAttachment(Constants.VERSION_KEY));
+
+        out.writeUTF(inv.getMethodName());
+        out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes()));
+        Object[] args = inv.getArguments();
+        if (args != null)
+        for (int i = 0; i < args.length; i++){
+            out.writeObject(encodeInvocationArgument(channel, inv, i));
+        }
+        out.writeObject(inv.getAttachments());
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException {
+        RpcInvocation inv = new RpcInvocation();
+
+        inv.setAttachment(Constants.DUBBO_VERSION_KEY, in.readUTF());
+        inv.setAttachment(Constants.PATH_KEY, in.readUTF());
+        inv.setAttachment(Constants.VERSION_KEY, in.readUTF());
+
+        inv.setMethodName(in.readUTF());
+        try {
+            Object[] args;
+            Class<?>[] pts;
+            String desc = in.readUTF();
+            if (desc.length() == 0) {
+                pts = EMPTY_CLASS_ARRAY;
+                args = EMPTY_OBJECT_ARRAY;
+            } else {
+                pts = ReflectUtils.desc2classArray(desc);
+                args = new Object[pts.length];
+                for (int i = 0; i < args.length; i++){
+                    try{
+                        args[i] = in.readObject(pts[i]);
+                    }catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+            inv.setParameterTypes(pts);
+            
+            Map<String, String> map = (Map<String, String>) in.readObject(Map.class);
+            if (map != null && map.size() > 0) {
+                Map<String, String> attachment = inv.getAttachments();
+                if (attachment == null) {
+                    attachment = new HashMap<String, String>();
+                }
+                attachment.putAll(map);
+                inv.setAttachments(attachment);
+            }
+            //decode argument ,may be callback
+            for (int i = 0; i < args.length; i++){
+                args[i] = decodeInvocationArgument(channel, inv, pts, i, args[i]);
+            }
+            
+            inv.setArguments(args);
+            
+        } catch (ClassNotFoundException e) {
+            throw new IOException(StringUtils.toString("Read invocation data failed.", e));
+        }
+        return inv;
+    }
+
+    @Override
+    protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException {
+        Result result = (Result) data;
+
+        Throwable th = result.getException();
+        if (th == null) {
+            Object ret = result.getValue();
+            if (ret == null) {
+                out.writeByte(RESPONSE_NULL_VALUE);
+            } else {
+                out.writeByte(RESPONSE_VALUE);
+                out.writeObject(ret);
+            }
+        } else {
+            out.writeByte(RESPONSE_WITH_EXCEPTION);
+            out.writeObject(th);
+        }
+    }

+    
+    @Override
+    protected Object decodeResponseData(Channel channel, ObjectInput in, Object request) throws IOException {
+        Invocation invocation = (Invocation) request;

+        RpcResult result = new RpcResult();
+
+        byte flag = in.readByte();
+        switch (flag) {
+            case RESPONSE_NULL_VALUE:
+                break;
+            case RESPONSE_VALUE:
+                try {
+                    Type[] returnType = RpcUtils.getReturnTypes(invocation);

+                    result.setValue(returnType == null || returnType.length == 0 ? in.readObject() : 

+                        (returnType.length == 1 ? in.readObject((Class<?>)returnType[0]) 

+                                : in.readObject((Class<?>)returnType[0], returnType[1])));
+                } catch (ClassNotFoundException e) {
+                    throw new IOException(StringUtils.toString("Read response data failed.", e));
+                }
+                break;
+            case RESPONSE_WITH_EXCEPTION:
+                try {
+                    Object obj = in.readObject();
+                    if (obj instanceof Throwable == false) throw new IOException("Response data error, expect Throwable, but get " + obj);
+                    result.setException((Throwable) obj);
+                } catch (ClassNotFoundException e) {
+                    throw new IOException(StringUtils.toString("Read response data failed.", e));
+                }
+                break;
+            default:
+                throw new IOException("Unknown result flag, expect '0' '1' '2', get " + flag);
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboExporter.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboExporter.java
new file mode 100644
index 0000000..e965da9
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboExporter.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;

+

+import java.util.Map;

+

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.protocol.AbstractExporter;

+

+/**

+ * DubboExporter

+ * 

+ * @author william.liangf

+ */

+public class DubboExporter<T> extends AbstractExporter<T> {

+

+    private final String                        key;

+

+    private final Map<String, Exporter<?>> exporterMap;

+

+    public DubboExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap){

+        super(invoker);

+        this.key = key;

+        this.exporterMap = exporterMap;

+    }

+

+    @Override

+    public void unexport() {

+        super.unexport();

+        exporterMap.remove(key);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.java
new file mode 100644
index 0000000..d32ab7f
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.java
@@ -0,0 +1,157 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;

+

+import java.util.Set;

+import java.util.concurrent.locks.ReentrantLock;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.AtomicPositiveInteger;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.TimeoutException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;

+

+/**

+ * DubboInvoker

+ * 

+ * @author william.liangf

+ * @author chao.liuc

+ */

+public class DubboInvoker<T> extends AbstractInvoker<T> {

+

+    private final ExchangeClient[]      clients;

+

+    private final AtomicPositiveInteger index = new AtomicPositiveInteger();

+

+    private final String                version;

+    

+    private final ReentrantLock     destroyLock = new ReentrantLock();

+    

+    private final Set<Invoker<?>> invokers;

+    

+    public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients){

+        this(serviceType, url, clients, null);

+    }

+    

+    public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers){

+        super(serviceType, url, new String[] {Constants.INTERFACE_KEY, Constants.GROUP_KEY, Constants.TOKEN_KEY, Constants.TIMEOUT_KEY});

+        this.clients = clients;

+        // get version.

+        this.version = url.getParameter(Constants.VERSION_KEY, "0.0.0");

+        this.invokers = invokers; 

+    }

+

+    @Override

+    protected Result doInvoke(final Invocation invocation) throws Throwable {

+        RpcInvocation inv = null;

+        final String methodName  ;

+        if(Constants.$INVOKE.equals(invocation.getMethodName()) 

+                && invocation.getArguments() != null 

+                && invocation.getArguments().length >0 

+                && invocation.getArguments()[0] != null){

+            inv = (RpcInvocation) invocation;

+            //the frist argument must be real method name;

+            methodName = invocation.getArguments()[0].toString();

+        }else {

+            inv = new RpcInvocation(invocation);

+            methodName = invocation.getMethodName();

+        }

+        inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());

+        inv.setAttachment(Constants.VERSION_KEY, version);

+        

+        ExchangeClient currentClient;

+        if (clients.length == 1) {

+            currentClient = clients[0];

+        } else {

+            currentClient = clients[index.getAndIncrement() % clients.length];

+        }

+        try {

+            // 不可靠异步

+            boolean isAsync = getUrl().getMethodParameter(methodName, Constants.ASYNC_KEY, false);

+            int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);

+            if (isAsync) { 

+                boolean isReturn = getUrl().getMethodParameter(methodName, Constants.RETURN_KEY, true);

+                if (isReturn) {

+                    ResponseFuture future = currentClient.request(inv, timeout) ;

+                    RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));

+                } else {

+                    boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);

+                    currentClient.send(inv, isSent);

+                    RpcContext.getContext().setFuture(null);

+                }

+                return new RpcResult();

+            }

+            RpcContext.getContext().setFuture(null);

+            return (Result) currentClient.request(inv, timeout).get();

+        } catch (TimeoutException e) {

+            throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Failed to invoke remote invocation " + invocation + " to " + getUrl() + ", cause: " + e.getMessage(), e);

+        } catch (RemotingException e) {

+            throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote invocation " + invocation + " to " + getUrl() + ", cause: " + e.getMessage(), e);

+        }

+    }

+    

+    @Override

+    public boolean isAvailable() {

+        if (!super.isAvailable())

+            return false;

+        for (ExchangeClient client : clients){

+            if (client.isConnected() && !client.hasAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY)){

+                //cannot write == not Available ?

+                return true ;

+            }

+        }

+        return false;

+    }

+

+    public void destroy() {

+        //防止client被关闭多次.在connect per jvm的情况下,client.close方法会调用计数器-1,当计数器小于等于0的情况下,才真正关闭

+        if (super.isDestroyed()){

+            return ;

+        } else {

+            //dubbo check ,避免多次关闭

+            destroyLock.lock();

+            try{

+                if (super.isDestroyed()){

+                    return ;

+                }

+                super.destroy();

+                if (invokers != null){

+                    invokers.remove(this);

+                }

+                for (ExchangeClient client : clients) {

+                    try {

+                        client.close();

+                    } catch (Throwable t) {

+                        logger.warn(t.getMessage(), t);

+                    }

+                }

+                

+            }finally {

+                destroyLock.unlock();

+            }

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java
new file mode 100644
index 0000000..4d2b758
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java
@@ -0,0 +1,421 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import java.net.InetSocketAddress;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.locks.ReentrantLock;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.Transporter;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.remoting.exchange.Exchangers;

+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;

+
+/**
+ * dubbo protocol support.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class DubboProtocol extends AbstractProtocol {
+
+    public static final String NAME = "dubbo";
+
+    public static final String COMPATIBLE_CODEC_NAME = "dubbo1compatible";
+    
+    public static final int DEFAULT_PORT = 20880;

+    

+    public final ReentrantLock lock = new ReentrantLock();
+    
+    private final Map<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>(); // <host:port,Exchanger>

+    

+    private final Map<String, ReferenceCountExchangeClient> referenceClientMap = new ConcurrentHashMap<String, ReferenceCountExchangeClient>(); // <host:port,Exchanger>

+    

+    private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap = new ConcurrentHashMap<String, LazyConnectExchangeClient>();
+    
+    //consumer side export a stub service for dispatching event
+    //servicekey-stubmethods
+    private final ConcurrentMap<String, String> stubServiceMethodsMap = new ConcurrentHashMap<String, String>();

+    

+    private static final String IS_CALLBACK_SERVICE_INVOKE = "_isCallBackServiceInvoke";
+
+    private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
+        
+        public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
+            if (message instanceof Invocation) {
+                Invocation inv = (Invocation) message;
+                Invoker<?> invoker = getInvoker(channel, inv);
+                //如果是callback 需要处理高版本调用低版本的问题
+                if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))){
+                    String methodsStr = invoker.getUrl().getParameters().get("methods");
+                    boolean hasMethod = false;
+                    if (methodsStr == null || methodsStr.indexOf(",") == -1){
+                        hasMethod = inv.getMethodName().equals(methodsStr);
+                    } else {
+                        String[] methods = methodsStr.split(",");
+                        for (String method : methods){
+                            if (inv.getMethodName().equals(method)){
+                                hasMethod = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (!hasMethod){
+                        logger.warn(new IllegalStateException("The methodName "+inv.getMethodName()+" not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) +" ,invocation is :"+inv );
+                        return null;
+                    }
+                }
+                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
+                return invoker.invoke(inv);
+            }
+            throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
+        }
+
+        @Override
+        public void received(Channel channel, Object message) throws RemotingException {
+            if (message instanceof Invocation) {
+                reply((ExchangeChannel) channel, message);
+            } else {
+                super.received(channel, message);
+            }
+        }
+
+        @Override
+        public void connected(Channel channel) throws RemotingException {
+            invoke(channel, Constants.ON_CONNECT_KEY);
+        }
+
+        @Override
+        public void disconnected(Channel channel) throws RemotingException {
+            if(logger.isInfoEnabled()){
+                logger.info("disconected from "+ channel.getRemoteAddress() + ",url:" + channel.getUrl());
+            }
+            invoke(channel, Constants.ON_DISCONNECT_KEY);
+        }
+        
+        private void invoke(Channel channel, String methodKey) {
+            Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
+            if (invocation != null) {
+                try {
+                    received(channel, invocation);
+                } catch (Throwable t) {
+                    logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
+                }
+            }
+        }
+        
+        private Invocation createInvocation(Channel channel, URL url, String methodKey) {
+            String method = url.getParameter(methodKey);
+            if (method == null || method.length() == 0) {
+                return null;
+            }
+            RpcInvocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);
+            invocation.setAttachment(Constants.PATH_KEY, url.getPath());
+            invocation.setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));
+            invocation.setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));
+            invocation.setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY));
+            if (url.getParameter(Constants.STUB_EVENT_KEY, false)){
+                invocation.setAttachment(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString());
+            }
+            return invocation;
+        }
+    };
+    
+    private static DubboProtocol INSTANCE;
+
+    public DubboProtocol() {
+        INSTANCE = this;
+    }
+    
+    public static DubboProtocol getDubboProtocol() {
+        if (INSTANCE == null) {
+            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(DubboProtocol.NAME); // load
+        }
+        return INSTANCE;
+    }
+
+    public Collection<ExchangeServer> getServers() {
+        return Collections.unmodifiableCollection(serverMap.values());
+    }
+
+    public Collection<Exporter<?>> getExporters() {
+        return Collections.unmodifiableCollection(exporterMap.values());
+    }

+    

+    Map<String, Exporter<?>> getExporterMap(){

+        return exporterMap;

+    }

+    

+    private boolean isClientSide(Channel channel) {

+        InetSocketAddress address = channel.getRemoteAddress();

+        URL url = channel.getUrl();

+        return url.getPort() == address.getPort() && 

+                    NetUtils.filterLocalHost(channel.getUrl().getIp())

+                    .equals(NetUtils.filterLocalHost(address.getAddress().getHostAddress()));

+    }

+    

+    Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException{

+        boolean isCallBackServiceInvoke = false;

+        boolean isStubServiceInvoke = false;

+        int port = channel.getLocalAddress().getPort();

+        String path = inv.getAttachments().get(Constants.PATH_KEY);

+        //如果是客户端的回调服务.

+        isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getAttachments().get(Constants.STUB_EVENT_KEY));

+        if (isStubServiceInvoke){

+            port = channel.getRemoteAddress().getPort();

+        }

+        //callback

+        isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke;

+        if(isCallBackServiceInvoke){

+            path = inv.getAttachments().get(Constants.PATH_KEY)+"."+inv.getAttachments().get(Constants.CALLBACK_SERVICE_KEY);

+            inv.getAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString());

+        }

+        String serviceKey = serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.GROUP_KEY));

+

+        DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);

+        

+        if (exporter == null)

+            throw new RemotingException(channel, "Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + inv);

+

+        return exporter.getInvoker();

+    }

+    

+    public Collection<Invoker<?>> getInvokers() {
+        return Collections.unmodifiableCollection(invokers);
+    }
+
+    public int getDefaultPort() {
+        return DEFAULT_PORT;
+    }
+
+    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+        URL url = invoker.getUrl().addParameterIfAbsent(Constants.DOWNSTREAM_CODEC_KEY, DubboCodec.NAME);
+        
+        // export service.
+        String key = serviceKey(url);
+        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
+        exporterMap.put(key, exporter);
+        
+        //export an stub service for dispaching event
+        Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY,Constants.DEFAULT_STUB_EVENT);
+        Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
+        if (isStubSupportEvent && !isCallbackservice){
+            String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
+            if (stubServiceMethods == null || stubServiceMethods.length() == 0 ){
+                if (logger.isWarnEnabled()){
+                    logger.warn( new IllegalStateException("consumer ["+url.getParameter(Constants.INTERFACE_KEY)+"], has set stubproxy support event ,but no stub methods founded."));
+                }
+            } else {
+                stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
+            }
+        }

+

+        openServer(url);

+        

+        return exporter;

+    }

+    

+    private void openServer(URL url) {

+        // find server.

+        String key = url.getAddress();

+        //client 也可以暴露一个只有server可以调用的服务。

+        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);

+        if (isServer && ! serverMap.containsKey(key)) {

+            serverMap.put(key, getServer(url));

+        }

+    }

+    
+    private ExchangeServer getServer(URL url) {

+        //默认开启server关闭时发送readonly事件

+        url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
+        String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
+
+        if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
+            throw new RpcException("Unsupported server type: " + str + ", url: " + url);
+
+        url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
+        ExchangeServer server;
+        try {
+            server = Exchangers.bind(url, requestHandler);
+        } catch (RemotingException e) {
+            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
+        }
+        str = url.getParameter(Constants.CLIENT_KEY);
+        if (str != null && str.length() > 0) {
+            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
+            if (!supportedTypes.contains(str)) {
+                throw new RpcException("Unsupported client type: " + str);
+            }
+        }
+        return server;
+    }

+
+    public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {

+        // create rpc invoker.
+        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
+        invokers.add(invoker);

+        return invoker;
+    }

+    

+    private ExchangeClient[] getClients(URL url){

+        //是否共享连接

+        boolean service_share_connect = false;

+        int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);

+        //如果connections不配置,则共享连接,否则每服务每连接

+        if (connections == 0){

+            service_share_connect = true;

+            connections = 1;

+        }

+        

+        ExchangeClient[] clients = new ExchangeClient[connections];

+        for (int i = 0; i < clients.length; i++) {

+            if (service_share_connect){

+                clients[i] = getSharedClient(url);

+            } else {

+                clients[i] = initClient(url);

+            }

+        }

+        return clients;

+    }

+    

+    /**

+     *获取共享连接 

+     */

+    private ExchangeClient getSharedClient(URL url){

+        String key = url.getAddress();

+        ReferenceCountExchangeClient client = referenceClientMap.get(key);

+        if ( client != null ){

+            if ( !client.isClosed()){

+                client.incrementAndGetCount();

+                return client;

+            } else {

+//                logger.warn(new IllegalStateException("client is closed,but stay in clientmap .client :"+ client));

+                referenceClientMap.remove(key);

+            }

+        }

+        ExchangeClient exchagneclient = initClient(url);

+        

+        client = new ReferenceCountExchangeClient(exchagneclient, ghostClientMap);

+        referenceClientMap.put(key, client);

+        ghostClientMap.remove(key);

+        return client; 

+    }
+
+    /**

+     * 创建新连接.

+     */

+    private ExchangeClient initClient(URL url) {

+        

+        // client type setting.
+        String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
+
+        String version = url.getParameter(Constants.DUBBO_VERSION_KEY);
+        boolean compatible = (version != null && version.startsWith("1.0."));
+        url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() && compatible ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
+        
+        // BIO存在严重性能问题,暂时不允许使用
+        if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
+            throw new RpcException("Unsupported client type: " + str + "," +
+                    " supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
+        }
+        

+        ExchangeClient client ;

+        try {
+            //设置连接应该是lazy的 
+            if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)){
+                client = new LazyConnectExchangeClient(url ,requestHandler);
+            } else {
+                client = Exchangers.connect(url ,requestHandler);
+            }

+        } catch (RemotingException e) {

+            throw new RpcException("Fail to create remoting client for service(" + url

+                    + "): " + e.getMessage(), e);

+        }

+        return client;

+    }
+
+    public void destroy() {
+        super.destroy();
+        for (String key : new ArrayList<String>(serverMap.keySet())) {
+            ExchangeServer server = serverMap.remove(key);
+            if (server != null) {
+                try {
+                    if (logger.isInfoEnabled()) {
+                        logger.info("Close dubbo server: " + server.getLocalAddress());
+                    }
+                    server.close(getServerShutdownTimeout());
+                } catch (Throwable t) {
+                    logger.warn(t.getMessage(), t);
+                }
+            }
+        }

+        

+        for (String key : new ArrayList<String>(referenceClientMap.keySet())) {

+            ExchangeClient client = referenceClientMap.remove(key);

+            if (client != null) {

+                try {

+                    if (logger.isInfoEnabled()) {

+                        logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress());

+                    }

+                    client.close();

+                } catch (Throwable t) {

+                    logger.warn(t.getMessage(), t);

+                }

+            }

+        }

+        

+        for (String key : new ArrayList<String>(ghostClientMap.keySet())) {

+            ExchangeClient client = ghostClientMap.remove(key);

+            if (client != null) {

+                try {

+                    if (logger.isInfoEnabled()) {

+                        logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress());

+                    }

+                    client.close();

+                } catch (Throwable t) {

+                    logger.warn(t.getMessage(), t);

+                }

+            }

+        }
+        stubServiceMethodsMap.clear();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java
new file mode 100644
index 0000000..c213116
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;

+

+import java.util.concurrent.ExecutionException;

+import java.util.concurrent.Future;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.TimeoutException;

+

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * FutureAdapter

+ * 

+ * @author william.liangf

+ */

+public class FutureAdapter<V> implements Future<V> {

+    

+    private final ResponseFuture future;

+

+    public FutureAdapter(ResponseFuture future){

+        this.future = future;

+    }

+

+    public ResponseFuture getFuture() {

+        return future;

+    }

+

+    public boolean cancel(boolean mayInterruptIfRunning) {

+        return false;

+    }

+

+    public boolean isCancelled() {

+        return false;

+    }

+

+    public boolean isDone() {

+        return future.isDone();

+    }

+

+    @SuppressWarnings("unchecked")

+    public V get() throws InterruptedException, ExecutionException {

+        try {

+            return (V) (((Result) future.get()).recreate());

+        } catch (RemotingException e) {

+            throw new ExecutionException(e.getMessage(), e);

+        } catch (Throwable e) {

+            throw new RpcException(e);

+        }

+    }

+

+    @SuppressWarnings("unchecked")

+    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {

+        int timeoutInMillis = (int) unit.convert(timeout, TimeUnit.MILLISECONDS);

+        try {

+            return (V) (((Result) future.get(timeoutInMillis)).recreate());

+        } catch (com.alibaba.dubbo.remoting.TimeoutException e) {

+            throw new TimeoutException(StringUtils.toString(e));

+        } catch (RemotingException e) {

+            throw new ExecutionException(e.getMessage(), e);

+        } catch (Throwable e) {

+            throw new RpcException(e);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java
new file mode 100644
index 0000000..dd27a53
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java
@@ -0,0 +1,220 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import java.net.InetSocketAddress;

+import java.util.concurrent.atomic.AtomicLong;

+import java.util.concurrent.locks.Lock;

+import java.util.concurrent.locks.ReentrantLock;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.Parameters;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.exchange.Exchangers;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+
+/**
+ * dubbo protocol support class.
+ * 
+ * @author chao.liuc
+ */
+@SuppressWarnings("deprecation")

+final class LazyConnectExchangeClient implements ExchangeClient{
+
+    private final static Logger logger = LoggerFactory.getLogger(LazyConnectExchangeClient.class); 
+
+    private final URL                     url;
+    private final ExchangeHandler         requestHandler;
+    private volatile ExchangeClient       client;
+    private final Lock                    connectLock = new ReentrantLock();
+    //lazy connect 如果没有初始化时的连接状态
+    private final boolean                 initialState ;

+    

+    protected final  boolean requestWithWarning;

+    

+    //当调用时warning,出现这个warning,表示程序可能存在bug.

+    static final  String REQUEST_WITH_WARNING_KEY = "lazyclient_request_with_warning";

+    

+    private AtomicLong warningcount = new AtomicLong(0);

+    

+    public LazyConnectExchangeClient(URL url, ExchangeHandler requestHandler) {
+        //lazy connect ,need set send.reconnect = true, to avoid channel bad status. 
+        this.url = url.addParameter(Constants.SEND_RECONNECT_KEY, Boolean.TRUE.toString());
+        this.requestHandler = requestHandler;
+        this.initialState = url.getParameter(Constants.LAZY_CONNECT_INITIAL_STATE_KEY,Constants.DEFAULT_LAZY_CONNECT_INITIAL_STATE);

+        this.requestWithWarning = url.getParameter(REQUEST_WITH_WARNING_KEY, false);
+    }

+    

+
+    private void initClient() throws RemotingException {
+        if (client != null )
+            return;
+        if (logger.isInfoEnabled()) {
+            logger.info("Lazy connect to " + url);
+        }
+        connectLock.lock();
+        try {
+            if (client != null)
+                return;
+            this.client = Exchangers.connect(url, requestHandler);
+        } finally {
+            connectLock.unlock();
+        }
+    }
+
+    public ResponseFuture request(Object request) throws RemotingException {

+        warning(request);
+        initClient();
+        return client.request(request);
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    public InetSocketAddress getRemoteAddress() {
+        if (client == null){

+            return InetSocketAddress.createUnresolved(url.getHost(), url.getPort());

+        } else {

+            return client.getRemoteAddress();

+        }
+    }
+    public ResponseFuture request(Object request, int timeout) throws RemotingException {

+        warning(request);
+        initClient();

+        return client.request(request, timeout);
+    }

+    

+    /**

+     * 如果配置了调用warning,则每调用5000次warning一次.

+     * @param request

+     */

+    private void warning(Object request){

+        if (requestWithWarning ){

+            if (warningcount.get() % 5000 == 0){

+                logger.warn(new IllegalStateException("safe guard client , should not be called ,must have a bug."));

+            }

+            warningcount.incrementAndGet() ;

+        }

+    }

+    

+    public ChannelHandler getChannelHandler() {
+        checkClient();
+        return client.getChannelHandler();

+    }
+
+    public boolean isConnected() {
+        if (client == null) {
+            return initialState;
+        } else {
+            return client.isConnected();
+        }
+    }
+
+    public InetSocketAddress getLocalAddress() {
+        if (client == null){

+            return InetSocketAddress.createUnresolved(NetUtils.getLocalHost(), 0);

+        } else {

+            return client.getLocalAddress();

+        }
+    }
+
+    public ExchangeHandler getExchangeHandler() {
+        return requestHandler;
+    }
+
+    public void send(Object message) throws RemotingException {
+        initClient();
+        client.send(message);
+    }
+
+    public void send(Object message, boolean sent) throws RemotingException {
+        initClient();
+        client.send(message, sent);
+    }
+
+    public boolean isClosed() {
+        if (client != null)
+            return client.isClosed();
+        else
+            return true;
+    }
+
+    public void close() {
+        if (client != null)
+            client.close();
+    }
+
+    public void close(int timeout) {
+        if (client != null)
+            client.close(timeout);
+    }
+
+    public void reset(URL url) {
+        checkClient();
+        client.reset(url);
+    }

+    

+    @Deprecated

+    public void reset(Parameters parameters){

+        reset(getUrl().addParameters(parameters.getParameters()));

+    }
+
+    public void reconnect() throws RemotingException {
+        checkClient();
+        client.reconnect();
+    }
+
+    public Object getAttribute(String key) {
+        if (client == null){

+            return null;

+        } else {

+            return client.getAttribute(key);

+        }
+    }
+
+    public void setAttribute(String key, Object value) {
+        checkClient();
+        client.setAttribute(key, value);
+    }
+
+    public void removeAttribute(String key) {
+        checkClient();
+        client.removeAttribute(key);
+    }
+
+    public boolean hasAttribute(String key) {
+        if (client == null){

+            return false;

+        } else {
+            return client.hasAttribute(key);

+        }
+    }
+
+    private void checkClient() {
+        if (client == null) {
+            throw new IllegalStateException(
+                    "LazyConnectExchangeClient state error. the client has not be init .url:" + url);
+        }
+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
new file mode 100644
index 0000000..ccfb35c
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
@@ -0,0 +1,172 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import java.net.InetSocketAddress;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.Parameters;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+
+/**
+ * dubbo protocol support class.
+ * 
+ * @author chao.liuc
+ */
+@SuppressWarnings("deprecation")

+final class ReferenceCountExchangeClient implements ExchangeClient {
+
+    private ExchangeClient client;

+    

+    private final URL url;

+    

+//    private final ExchangeHandler handler;

+    

+    private final AtomicInteger refenceCount = new AtomicInteger(0);

+    

+    private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap;

+    

+    

+    public ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap) {

+        this.client = client;

+        refenceCount.incrementAndGet();

+        this.url = client.getUrl();

+        if (ghostClientMap == null){

+            throw new IllegalStateException("ghostClientMap can not be null, url: " + url);

+        }

+        this.ghostClientMap = ghostClientMap;

+    }
+
+    public void reset(URL url) {

+        client.reset(url);

+    }

+

+    public ResponseFuture request(Object request) throws RemotingException {

+        return client.request(request);

+    }

+

+    public URL getUrl() {

+        return client.getUrl();

+    }

+

+    public InetSocketAddress getRemoteAddress() {

+        return client.getRemoteAddress();

+    }

+

+    public ChannelHandler getChannelHandler() {

+        return client.getChannelHandler();

+    }

+

+    public ResponseFuture request(Object request, int timeout) throws RemotingException {

+        return client.request(request, timeout);

+    }

+

+    public boolean isConnected() {

+        return client.isConnected();

+    }

+

+    public void reconnect() throws RemotingException {

+        client.reconnect();

+    }

+

+    public InetSocketAddress getLocalAddress() {

+        return client.getLocalAddress();

+    }

+

+    public boolean hasAttribute(String key) {

+        return client.hasAttribute(key);

+    }

+

+    public void reset(Parameters parameters) {

+        client.reset(parameters);

+    }

+

+    public void send(Object message) throws RemotingException {

+        client.send(message);

+    }

+

+    public ExchangeHandler getExchangeHandler() {

+        return client.getExchangeHandler();

+    }

+

+    public Object getAttribute(String key) {

+        return client.getAttribute(key);

+    }

+

+    public void send(Object message, boolean sent) throws RemotingException {

+        client.send(message, sent);

+    }

+

+    public void setAttribute(String key, Object value) {

+        client.setAttribute(key, value);

+    }

+

+    public void removeAttribute(String key) {

+        client.removeAttribute(key);

+    }

+    /* 

+     * close方法将不再幂等,调用需要注意.

+     */

+    public void close() {

+        close(0);

+    }

+

+    public void close(int timeout) {

+        if (refenceCount.decrementAndGet() <= 0){

+            if (timeout == 0){

+                client.close();

+            } else {

+                client.close(timeout);

+            }

+            client = replaceWithLazyClient();

+        }

+    }

+    

+    //幽灵client,

+    private LazyConnectExchangeClient replaceWithLazyClient(){

+        //这个操作只为了防止程序bug错误关闭client做的防御措施,初始client必须为false状态

+        URL lazyUrl = url.addParameter(Constants.LAZY_CONNECT_INITIAL_STATE_KEY, Boolean.FALSE)

+                .addParameter(Constants.RECONNECT_KEY, Boolean.FALSE)

+                .addParameter(Constants.SEND_RECONNECT_KEY, Boolean.TRUE.toString())

+                .addParameter("warning", Boolean.TRUE.toString())

+                .addParameter(LazyConnectExchangeClient.REQUEST_WITH_WARNING_KEY, true)

+                .addParameter("_client_memo", "referencecounthandler.replacewithlazyclient");

+        

+        String key = url.getAddress();

+        //最差情况下只有一个幽灵连接

+        LazyConnectExchangeClient gclient = ghostClientMap.get(key);

+        if (gclient == null || gclient.isClosed()){

+            gclient = new LazyConnectExchangeClient(lazyUrl, client.getExchangeHandler());

+            ghostClientMap.put(key, gclient);

+        }

+        return gclient;

+    }

+

+    public boolean isClosed() {

+        return client.isClosed();

+    }

+    

+    public void incrementAndGetCount(){

+        refenceCount.incrementAndGet();

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
new file mode 100644
index 0000000..2b41eb7
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
@@ -0,0 +1,202 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.filter;

+

+import java.lang.reflect.InvocationTargetException;

+import java.lang.reflect.Method;

+import java.util.concurrent.Future;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.remoting.exchange.ResponseCallback;

+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.StaticContext;

+import com.alibaba.dubbo.rpc.protocol.dubbo.FutureAdapter;

+

+/**

+ * EventFilter

+ * @author chao.liuc

+ * @author william.liangf

+ */

+@Activate(group = Constants.CONSUMER)

+public class FutureFilter implements Filter {

+

+    protected static final Logger logger = LoggerFactory.getLogger(FutureFilter.class);

+

+    public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {

+        fireInvokeCallback(invoker, invocation);

+        //需要在调用前配置好是否有返回值,已供invoker判断是否需要返回future.

+        Result result = invoker.invoke(invocation);

+        if (invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) {

+            asyncCallback(invoker, invocation);

+        } else {

+            syncCallback(invoker, invocation, result);

+        }

+        return result;

+    }

+

+    private void syncCallback(final Invoker<?> invoker, final Invocation invocation, final Result result) {

+        if (result.hasException()) {

+            fireThrowCallback(invoker, invocation, result.getException());

+        } else {

+            fireReturnCallback(invoker, invocation, result.getValue());

+        }

+    }

+    

+    private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {

+        Future<?> f = RpcContext.getContext().getFuture();

+        if (f instanceof FutureAdapter) {

+            ResponseFuture future = ((FutureAdapter<?>)f).getFuture();

+            future.setCallback(new ResponseCallback() {

+                public void done(Object rpcResult) {

+                    if (rpcResult == null){

+                        logger.error(new IllegalStateException("invalid result value : null, expected "+Result.class.getName()));

+                        return;

+                    }

+                    ///must be rpcResult

+                    if (! (rpcResult instanceof Result)){

+                        logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected "+Result.class.getName()));

+                        return;

+                    }

+                    Result result = (Result) rpcResult;

+                    if (result.hasException()) {

+                        fireThrowCallback(invoker, invocation, result.getException());

+                    } else {

+                        fireReturnCallback(invoker, invocation, result.getValue());

+                    }

+                }

+                public void caught(Throwable exception) {

+                    fireThrowCallback(invoker, invocation, exception);

+                }

+            });

+        }

+    }

+

+    private void fireInvokeCallback(final Invoker<?> invoker, final Invocation invocation) {

+        final Method onInvokeMethod = (Method)StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_INVOKE_METHOD_KEY));

+        final Object onInvokeInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_INVOKE_INSTANCE_KEY));

+        

+        if (onInvokeMethod == null  &&  onInvokeInst == null ){

+            return ;

+        }

+        if (onInvokeMethod == null  ||  onInvokeInst == null ){

+            throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() +" has a onreturn callback config , but no such "+(onInvokeMethod == null ? "method" : "instance")+" found. url:"+invoker.getUrl());

+        }

+        if (onInvokeMethod != null && ! onInvokeMethod.isAccessible()) {

+            onInvokeMethod.setAccessible(true);

+        }

+        

+        Object[] params = invocation.getArguments();

+        try {

+            onInvokeMethod.invoke(onInvokeInst, params);

+        } catch (InvocationTargetException e) {

+            fireThrowCallback(invoker, invocation, e.getTargetException());

+        } catch (Throwable e) {

+            fireThrowCallback(invoker, invocation, e);

+        }

+    }

+    

+    private void fireReturnCallback(final Invoker<?> invoker, final Invocation invocation, final Object result) {

+        final Method onReturnMethod = (Method)StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_RETURN_METHOD_KEY));

+        final Object onReturnInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_RETURN_INSTANCE_KEY));

+

+        //not set onreturn callback

+        if (onReturnMethod == null  &&  onReturnInst == null ){

+            return ;

+        }

+        

+        if (onReturnMethod == null  ||  onReturnInst == null ){

+            throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() +" has a onreturn callback config , but no such "+(onReturnMethod == null ? "method" : "instance")+" found. url:"+invoker.getUrl());

+        }

+        if (onReturnMethod != null && ! onReturnMethod.isAccessible()) {

+            onReturnMethod.setAccessible(true);

+        }

+        

+        Object[] args = invocation.getArguments();

+        Object[] params ;

+        Class<?>[] rParaTypes = onReturnMethod.getParameterTypes() ;

+        if (rParaTypes.length >1 ) {

+            if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)){

+                params = new Object[2];

+                params[0] = result;

+                params[1] = args ;

+            }else {

+                params = new Object[args.length + 1];

+                params[0] = result;

+                System.arraycopy(args, 0, params, 1, args.length);

+            }

+        } else {

+            params = new Object[] { result };

+        }

+        try {

+            onReturnMethod.invoke(onReturnInst, params);

+        } catch (InvocationTargetException e) {

+            fireThrowCallback(invoker, invocation, e.getTargetException());

+        } catch (Throwable e) {

+            fireThrowCallback(invoker, invocation, e);

+        }

+    }

+    

+    private void fireThrowCallback(final Invoker<?> invoker, final Invocation invocation, final Throwable exception) {

+        final Method onthrowMethod = (Method)StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_METHOD_KEY));

+        final Object onthrowInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_INSTANCE_KEY));

+

+        //没有设置onthrow callback.

+        if (onthrowMethod == null  &&  onthrowInst == null ){

+            return ;

+        }

+        if (onthrowMethod == null  ||  onthrowInst == null ){

+            throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() +" has a onthrow callback config , but no such "+(onthrowMethod == null ? "method" : "instance")+" found. url:"+invoker.getUrl());

+        }

+        if (onthrowMethod != null && ! onthrowMethod.isAccessible()) {

+            onthrowMethod.setAccessible(true);

+        }

+        Class<?>[] rParaTypes = onthrowMethod.getParameterTypes() ;

+        if (rParaTypes[0].isAssignableFrom(exception.getClass())){

+            try {

+                Object[] args = invocation.getArguments();

+                Object[] params;

+                

+                if (rParaTypes.length >1 ) {

+                    if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)){

+                        params = new Object[2];

+                        params[0] = exception;

+                        params[1] = args ;

+                    }else {

+                        params = new Object[args.length + 1];

+                        params[0] = exception;

+                        System.arraycopy(args, 0, params, 1, args.length);

+                    }

+                } else {

+                    params = new Object[] { exception };

+                }

+                onthrowMethod.invoke(onthrowInst,params);

+            } catch (Throwable e) {

+                logger.error(invocation.getMethodName() +".call back method invoke error . callback method :" + onthrowMethod + ", url:"+ invoker.getUrl(), e);

+            } 

+        } else {

+            logger.error(invocation.getMethodName() +".call back method invoke error . callback method :" + onthrowMethod + ", url:"+ invoker.getUrl(), exception);

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java
new file mode 100644
index 0000000..2630923
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java
@@ -0,0 +1,128 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.filter;

+

+import java.util.ArrayList;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.json.JSON;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * TraceFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.PROVIDER)

+public class TraceFilter implements Filter {

+    

+    private static final Logger logger = LoggerFactory.getLogger(TraceFilter.class);

+    

+    private static final String TRACE_MAX = "trace.max";

+    

+    private static final String TRACE_COUNT = "trace.count";

+    

+    private static final ConcurrentMap<String, Set<Channel>> tracers = new ConcurrentHashMap<String, Set<Channel>>();

+    

+    public static void addTracer(Class<?> type, String method, Channel channel, int max) {

+        channel.setAttribute(TRACE_MAX, max);

+        channel.setAttribute(TRACE_COUNT, new AtomicInteger());

+        String key = method != null && method.length() > 0 ? type.getName() + "." + method : type.getName();

+        Set<Channel> channels = tracers.get(key);

+        if (channels == null) {

+            tracers.putIfAbsent(key, new ConcurrentHashSet<Channel>());

+            channels = tracers.get(key);

+        }

+        channels.add(channel);

+    }

+    

+    public static void removeTracer(Class<?> type, String method, Channel channel) {

+        channel.removeAttribute(TRACE_MAX);

+        channel.removeAttribute(TRACE_COUNT);

+        String key = method != null && method.length() > 0 ? type.getName() + "." + method : type.getName();

+        Set<Channel> channels = tracers.get(key);

+        if (channels != null) {

+            channels.remove(channel);

+        }

+    }

+    

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        long start = System.currentTimeMillis();

+        Result result = invoker.invoke(invocation);

+        long end = System.currentTimeMillis();

+        if (tracers.size() > 0) {

+            String key = invoker.getInterface().getName() + "." + invocation.getMethodName();

+            Set<Channel> channels = tracers.get(key);

+            if (channels == null || channels.size() == 0) {

+                key = invoker.getInterface().getName();

+                channels = tracers.get(key);

+            }

+            if (channels != null && channels.size() > 0) {

+                for (Channel channel : new ArrayList<Channel>(channels)) {

+                    if (channel.isConnected()) {

+                        try {

+                            int max = 1;

+                            Integer m = (Integer) channel.getAttribute(TRACE_MAX);

+                            if (m != null) {

+                                max = (int) m;

+                            }

+                            int count = 0;

+                            AtomicInteger c = (AtomicInteger) channel.getAttribute(TRACE_COUNT);

+                            if (c == null) {

+                                c = new AtomicInteger();

+                                channel.setAttribute(TRACE_COUNT, c);

+                            }

+                            count = c.getAndIncrement();

+                            if (count < max) {

+                                String prompt = channel.getUrl().getParameter(Constants.PROMPT_KEY, Constants.DEFAULT_PROMPT);

+                                channel.send("\r\n" + RpcContext.getContext().getRemoteAddress() + " -> "  

+                                         + invoker.getInterface().getName() 

+                                         + "." + invocation.getMethodName() 

+                                         + "(" + JSON.json(invocation.getArguments()) + ")" + " -> " + JSON.json(result.getValue())

+                                         + "\r\nelapsed: "+(end - start) +" ms."

+                                         + "\r\n\r\n" + prompt);

+                            }

+                            if(count >= max - 1) {

+                                channels.remove(channel);

+                            }

+                        } catch (Throwable e) {

+                            channels.remove(channel);

+                            logger.warn(e.getMessage(), e);

+                        }

+                    } else {

+                        channels.remove(channel);

+                    }

+                }

+            }

+        }

+        return result;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ClientsPageHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ClientsPageHandler.java
new file mode 100644
index 0000000..dd04d07
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ClientsPageHandler.java
@@ -0,0 +1,78 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.page;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * ClientsPageHandler

+ * 

+ * @author william.liangf

+ */

+public class ClientsPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        String port = url.getParameter("port");

+        int p = port == null || port.length() == 0 ? 0 : Integer.parseInt(port);

+        Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();

+        ExchangeServer server = null;

+        StringBuilder select = new StringBuilder();

+        if (servers != null && servers.size() > 0) {

+            if (servers.size() == 1) {

+                server = servers.iterator().next();

+                String address = server.getUrl().getAddress();

+                select.append(" &gt; " + NetUtils.getHostName(address) + "/" + address);

+            } else {

+                select.append(" &gt; <select onchange=\"window.location.href='clients.html?port=' + this.value;\">");

+                for (ExchangeServer s : servers) {

+                    int sp = s.getUrl().getPort();

+                    select.append("<option value=\">");

+                    select.append(sp);

+                    if (p == 0 && server == null || p == sp) {

+                        server = s;

+                        select.append("\" selected=\"selected");

+                    }

+                    select.append("\">");

+                    select.append(s.getUrl().getAddress());

+                    select.append("</option>");

+                }

+                select.append("</select>");

+            }

+        }

+        List<List<String>> rows = new ArrayList<List<String>>();

+        if (server != null) {

+            Collection<ExchangeChannel> channels = server.getExchangeChannels();

+            for (ExchangeChannel c : channels) {

+                List<String> row = new ArrayList<String>();

+                String address = NetUtils.toAddressString(c.getRemoteAddress());

+                row.add(NetUtils.getHostName(address) + "/" + address);

+                rows.add(row);

+            }

+        }

+        return new Page("<a href=\"servers.html\">Servers</a>" + select.toString() + " &gt; Clients", "Clients (" + rows.size() + ")", new String[]{"Client Address:"}, rows);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ServersPageHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ServersPageHandler.java
new file mode 100644
index 0000000..a02f506
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ServersPageHandler.java
@@ -0,0 +1,56 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.page;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.container.page.Menu;

+import com.alibaba.dubbo.container.page.Page;

+import com.alibaba.dubbo.container.page.PageHandler;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * ServersPageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "Servers", desc="Show exported service servers.", order = 14000)

+public class ServersPageHandler implements PageHandler {

+

+    public Page handle(URL url) {

+        List<List<String>> rows = new ArrayList<List<String>>();

+        Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();

+        int clientCount = 0;

+        if (servers != null && servers.size() > 0) {

+            for (ExchangeServer s : servers) {

+                List<String> row = new ArrayList<String>();

+                String address = s.getUrl().getAddress();

+                row.add(NetUtils.getHostName(address) + "/" + address);

+                int clientSize = s.getExchangeChannels().size();

+                clientCount += clientSize;

+                row.add("<a href=\"clients.html?port=" + s.getUrl().getPort() + "\">Clients(" + clientSize + ")</a>");

+                rows.add(row);

+            }

+        }

+        return new Page("Servers", "Servers (" + rows.size() + ")", new String[]{"Server Address:", "Clients(" + clientCount + ")"}, rows);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java
new file mode 100644
index 0000000..ab8caac
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java
@@ -0,0 +1,59 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.status;

+

+import java.util.Collection;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * ServerStatusChecker

+ * 

+ * @author william.liangf

+ */

+@Activate

+public class ServerStatusChecker implements StatusChecker {

+

+    public Status check() {

+        Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();

+        if (servers == null || servers.size() == 0) {

+            return new Status(Status.Level.UNKNOWN);

+        }

+        Status.Level level = Status.Level.OK;

+        StringBuilder buf = new StringBuilder();

+        for (ExchangeServer server : servers) {

+            if (! server.isBound()) {

+                level = Status.Level.ERROR;

+                buf.setLength(0);

+                buf.append(server.getLocalAddress());

+                break;

+            }

+            if (buf.length() > 0) {

+                buf.append(",");

+            }

+            buf.append(server.getLocalAddress());

+            buf.append("(clients:");

+            buf.append(server.getChannels().size());

+            buf.append(")");

+        }

+        return new Status(level, buf.toString());

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java
new file mode 100644
index 0000000..a334d16
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java
@@ -0,0 +1,68 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.status;

+

+import java.util.Collection;

+import java.util.concurrent.Executor;

+import java.util.concurrent.ThreadPoolExecutor;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.remoting.ChannelHandler;

+import com.alibaba.dubbo.remoting.Server;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer;

+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * ThreadPoolStatusChecker

+ * 

+ * @author william.liangf

+ */

+@Activate

+public class ThreadPoolStatusChecker implements StatusChecker {

+

+    public Status check() {

+        Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();

+        if (servers == null || servers.size() == 0) {

+            return new Status(Status.Level.UNKNOWN);

+        }

+        for (Server server : servers) {

+            if (server instanceof HeaderExchangeServer) {

+                HeaderExchangeServer exchanger = (HeaderExchangeServer) server;

+                server = exchanger.getServer();

+            }

+            ChannelHandler handler = server.getChannelHandler();

+            if (handler instanceof WrappedChannelHandler) {

+                Executor executor = ((WrappedChannelHandler) handler).getExecutor();

+                if (executor instanceof ThreadPoolExecutor) {

+                    ThreadPoolExecutor tp = (ThreadPoolExecutor)executor;

+                    boolean ok = tp.getActiveCount() < tp.getMaximumPoolSize() - 1;

+                    return new Status(ok ? Status.Level.OK : Status.Level.WARN, 

+                            "max:" + tp.getMaximumPoolSize() 

+                            + ",core:" + tp.getCorePoolSize() 

+                            + ",largest:" + tp.getLargestPoolSize()

+                            + ",active:" + tp.getActiveCount() 

+                            + ",task:" + tp.getTaskCount());

+                }

+            }

+        }

+        return new Status(Status.Level.UNKNOWN);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandler.java
new file mode 100644
index 0000000..1f998bf
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandler.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * ChangeServiceTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "[service]", summary = "Change default service.", detail = "Change default service.")

+public class ChangeTelnetHandler implements TelnetHandler {

+    

+    public static final String SERVICE_KEY = "telnet.service";

+

+    public String telnet(Channel channel, String message) {

+        if (message == null || message.length() == 0) {

+            return "Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService";

+        }

+        StringBuilder buf = new StringBuilder();

+        if (message.equals("/") || message.equals("..")) {

+            String service = (String) channel.getAttribute(SERVICE_KEY);

+            channel.removeAttribute(SERVICE_KEY);

+            buf.append("Cancelled default service " + service + ".");

+        } else {

+            boolean found = false;

+            for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {

+                if (message.equals(exporter.getInvoker().getInterface().getSimpleName())

+                        || message.equals(exporter.getInvoker().getInterface().getName())

+                        || message.equals(exporter.getInvoker().getUrl().getPath())) {

+                    found = true;

+                    break;

+                }

+            }

+            if (found) {

+                channel.setAttribute(SERVICE_KEY, message);

+                buf.append("Used the " + message + " as default.\r\nYou can cancel default service by command: cd /");

+            } else {

+                buf.append("No such service " + message);

+            }

+        }

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CountTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CountTelnetHandler.java
new file mode 100644
index 0000000..b58be30
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CountTelnetHandler.java
@@ -0,0 +1,166 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import java.lang.reflect.Method;

+import java.util.ArrayList;

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+import com.alibaba.dubbo.remoting.telnet.support.TelnetUtils;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcStatus;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * CountTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "[service] [method] [times]", summary = "Count the service.", detail = "Count the service.")

+public class CountTelnetHandler implements TelnetHandler {

+

+    public String telnet(final Channel channel, String message) {

+        String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);

+        if ((service == null || service.length() == 0)

+                && (message == null || message.length() == 0)) {

+            return "Please input service name, eg: \r\ncount XxxService\r\ncount XxxService xxxMethod\r\ncount XxxService xxxMethod 10\r\nor \"cd XxxService\" firstly.";

+        }

+        StringBuilder buf = new StringBuilder();

+        if (service != null && service.length() > 0) {

+            buf.append("Use default service " + service + ".\r\n");

+        }

+        String[] parts = message.split("\\s+");

+        String method;

+        String times;

+        if (service == null || service.length() == 0) {

+            service = parts.length > 0 ? parts[0] : null;

+            method = parts.length > 1 ? parts[1] : null;

+        } else {

+            method = parts.length > 0 ? parts[0] : null;

+        }

+        if (StringUtils.isInteger(method)) {

+            times = method;

+            method = null;

+        } else {

+            times = parts.length > 2 ? parts[2] : "1";

+        }

+        if (! StringUtils.isInteger(times)) {

+            return "Illegal times " + times + ", must be integer.";

+        }

+        final int t = Integer.parseInt(times);

+        Invoker<?> invoker = null;

+        for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {

+            if (service.equals(exporter.getInvoker().getInterface().getSimpleName())

+                    || service.equals(exporter.getInvoker().getInterface().getName())

+                    || service.equals(exporter.getInvoker().getUrl().getPath())) {

+                invoker = exporter.getInvoker();

+                break;

+            }

+        }

+        if (invoker != null) {

+            if (t > 0) {

+                final String mtd = method;

+                final Invoker<?> inv = invoker;

+                final String prompt = channel.getUrl().getParameter("prompt", "telnet");

+                Thread thread = new Thread(new Runnable() {

+                    public void run() {

+                        for (int i = 0; i < t; i ++) {

+                            String result = count(inv, mtd);

+                            try {

+                                channel.send("\r\n" + result);

+                            } catch (RemotingException e1) {

+                                return;

+                            }

+                            if (i < t - 1) {

+                                try {

+                                    Thread.sleep(1000);

+                                } catch (InterruptedException e) {

+                                }

+                            }

+                        }

+                        try {

+                            channel.send("\r\n" + prompt + "> ");

+                        } catch (RemotingException e1) {

+                            return;

+                        }

+                    }

+                }, "TelnetCount");

+                thread.setDaemon(true);

+                thread.start();

+            }

+        } else {

+            buf.append("No such service " + service);

+        }

+        return buf.toString();

+    }

+    

+    private String count(Invoker<?> invoker, String method) {

+        URL url = invoker.getUrl();

+        List<List<String>> table = new ArrayList<List<String>>();

+        List<String> header = new ArrayList<String>();

+        header.add("method");

+        header.add("total");

+        header.add("failed");

+        header.add("active");

+        header.add("average");

+        header.add("max");

+        if (method == null || method.length() == 0) {

+            for (Method m : invoker.getInterface().getMethods()) {

+                RpcStatus count = RpcStatus.getStatus(url, m.getName());

+                List<String> row = new ArrayList<String>();

+                row.add(m.getName());

+                row.add(String.valueOf(count.getTotal()));

+                row.add(String.valueOf(count.getFailed()));

+                row.add(String.valueOf(count.getActive()));

+                row.add(String.valueOf(count.getSucceededAverageElapsed()) + "ms");

+                row.add(String.valueOf(count.getSucceededMaxElapsed()) + "ms");

+                table.add(row);

+            }

+        } else {

+            boolean found = false;

+            for (Method m : invoker.getInterface().getMethods()) {

+                if (m.getName().equals(method)) {

+                    found = true;

+                    break;

+                }

+            }

+            if (found) {

+                RpcStatus count = RpcStatus.getStatus(url, method);

+                List<String> row = new ArrayList<String>();

+                row.add(method);

+                row.add(String.valueOf(count.getTotal()));

+                row.add(String.valueOf(count.getFailed()));

+                row.add(String.valueOf(count.getActive()));

+                row.add(String.valueOf(count.getSucceededAverageElapsed()) + "ms");

+                row.add(String.valueOf(count.getSucceededMaxElapsed()) + "ms");

+                table.add(row);

+            } else {

+                return "No such method " + method + " in class " + invoker.getInterface().getName();

+            }

+        }

+        return TelnetUtils.toTable(header, table);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandler.java
new file mode 100644
index 0000000..529c05b
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandler.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+

+/**

+ * CurrentServiceTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "", summary = "Print working default service.", detail = "Print working default service.")

+public class CurrentTelnetHandler implements TelnetHandler {

+    

+    public String telnet(Channel channel, String message) {

+        if (message.length() > 0) {

+            return "Unsupported parameter " + message + " for pwd.";

+        }

+        String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);

+        StringBuilder buf = new StringBuilder();

+        if (service == null || service.length() == 0) {

+            buf.append("/");

+        } else {

+            buf.append(service);

+        }

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java
new file mode 100644
index 0000000..684522e
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java
@@ -0,0 +1,127 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import java.lang.reflect.Method;

+import java.util.List;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.json.JSON;

+import com.alibaba.dubbo.common.utils.PojoUtils;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * InvokeTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "[service.]method(args)", summary = "Invoke the service method.", detail = "Invoke the service method.")

+public class InvokeTelnetHandler implements TelnetHandler {

+

+    @SuppressWarnings("unchecked")

+    public String telnet(Channel channel, String message) {

+        if (message == null || message.length() == 0) {

+            return "Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})";

+        }

+        StringBuilder buf = new StringBuilder();

+        String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);

+        if (service != null && service.length() > 0) {

+            buf.append("Use default service " + service + ".\r\n");

+        }

+        int i = message.indexOf("(");

+        if (i < 0 || ! message.endsWith(")")) {

+            return "Invalid parameters, format: service.method(args)";

+        }

+        String method = message.substring(0, i).trim();

+        String args = message.substring(i + 1, message.length() - 1).trim();

+        i = method.lastIndexOf(".");

+        if (i >= 0) {

+            service = method.substring(0, i).trim();

+            method = method.substring(i + 1).trim();

+        }

+        Invoker<?> invoker = null;

+        Method invokeMethod = null;

+        for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {

+            if (service == null || service.length() == 0) {

+                Method[] methods = exporter.getInvoker().getInterface().getMethods();

+                for (Method m : methods) {

+                    if (m.getName().equals(method)

+                            || ReflectUtils.getSignature(m.getName(), m.getParameterTypes()).equals(method)) {

+                        invoker = exporter.getInvoker();

+                        invokeMethod = m;

+                        break;

+                    }

+                }

+                if (invoker != null) {

+                    break;

+                }

+            } else {

+                if (service.equals(exporter.getInvoker().getInterface().getSimpleName())

+                        || service.equals(exporter.getInvoker().getInterface().getName())

+                        || service.equals(exporter.getInvoker().getUrl().getPath())) {

+                    invoker = exporter.getInvoker();

+                    Method[] methods = invoker.getInterface().getMethods();

+                    for (Method m : methods) {

+                        if (m.getName().equals(method)

+                                || ReflectUtils.getSignature(m.getName(), m.getParameterTypes()).equals(method)) {

+                            invokeMethod = m;

+                        }

+                    }

+                    break;

+                }

+            }

+        }

+        if (invoker != null) {

+            if (invokeMethod != null) {

+                List<Object> list;

+                try {

+                    list = (List<Object>) JSON.parse("[" + args + "]", List.class);

+                } catch (Throwable t) {

+                    return "Invalid json argument, cause: " + t.getMessage();

+                }

+                try {

+                    Object[] array = PojoUtils.realize(list.toArray(), invokeMethod.getParameterTypes());

+                    RpcContext.getContext().setLocalAddress(channel.getLocalAddress()).setRemoteAddress(channel.getRemoteAddress());

+                    long start = System.currentTimeMillis();

+                    Object result = invoker.invoke(new RpcInvocation(invokeMethod, array)).recreate();

+                    long end = System.currentTimeMillis();

+                    buf.append(JSON.json(result));

+                    buf.append("\r\nelapsed: ");

+                    buf.append(end - start);

+                    buf.append(" ms.");

+                } catch (Throwable t) {

+                    return "Failed to invoke method " + invokeMethod.getName() + ", cause: " + StringUtils.toString(t);

+                }

+            } else {

+                buf.append("No such method " + method + " in service " + service);

+            }

+        } else {

+            buf.append("No such service " + service);

+        }

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandler.java
new file mode 100644
index 0000000..2f60210
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandler.java
@@ -0,0 +1,100 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import java.lang.reflect.Method;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * ListTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "[-l] [service]", summary = "List services and methods.", detail = "List services and methods.")

+public class ListTelnetHandler implements TelnetHandler {

+

+    public String telnet(Channel channel, String message) {

+        StringBuilder buf = new StringBuilder();

+        String service = null;

+        boolean detail = false;

+        if (message.length() > 0) {

+            String[] parts = message.split("\\s+");

+            for (String part : parts) {

+                if ("-l".equals(part)) {

+                    detail = true;

+                } else {

+                    if (service != null && service.length() > 0) {

+                        return "Invaild parameter " + part;

+                    }

+                    service = part;

+                }

+            }

+        } else {

+            service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);

+            if (service != null && service.length() > 0) {

+                buf.append("Use default service " + service + ".\r\n");

+            }

+        }

+        if (service == null || service.length() == 0) {

+            for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {

+                if (buf.length() > 0) {

+                    buf.append("\r\n");

+                }

+                buf.append(exporter.getInvoker().getInterface().getName());

+                if (detail) {

+                    buf.append(" -> ");

+                    buf.append(exporter.getInvoker().getUrl());

+                }

+            }

+        } else {

+            Invoker<?> invoker = null;

+            for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {

+                if (service.equals(exporter.getInvoker().getInterface().getSimpleName())

+                        || service.equals(exporter.getInvoker().getInterface().getName())

+                        || service.equals(exporter.getInvoker().getUrl().getPath())) {

+                    invoker = exporter.getInvoker();

+                    break;

+                }

+            }

+            if (invoker != null) {

+                Method[] methods = invoker.getInterface().getMethods();

+                for (Method method : methods) {

+                    if (buf.length() > 0) {

+                        buf.append("\r\n");

+                    }

+                    if (detail) {

+                        buf.append(ReflectUtils.getName(method));

+                    } else {

+                        buf.append(method.getName());

+                    }

+                }

+            } else {

+                buf.append("No such service " + service);

+            }

+        }

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandler.java
new file mode 100644
index 0000000..e2c37df
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandler.java
@@ -0,0 +1,94 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;
+
+
+import java.io.File;

+import java.io.FileInputStream;

+import java.nio.ByteBuffer;

+import java.nio.channels.FileChannel;

+import java.text.SimpleDateFormat;

+import java.util.Date;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Level;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+
+/**
+ * LogTelnetHandler
+ * @author chao.liuc
+ * 
+ */

+@Activate
+@Help(parameter = "level", summary = "Change log level or show log ", detail = "Change log level or show log")
+public class LogTelnetHandler implements TelnetHandler {
+    
+    public static final String SERVICE_KEY = "telnet.log";
+
+    public String telnet(Channel channel, String message) {
+        long size = 0 ;
+        File file = LoggerFactory.getFile();
+        StringBuffer buf = new StringBuffer();
+        if (message == null || message.trim().length() == 0) {
+            buf.append("EXAMPLE: log error / log 100");
+        }else {
+            String str[] = message.split(" ");
+            if (! StringUtils.isInteger(str[0])){
+                LoggerFactory.setLevel(Level.valueOf(message.toUpperCase()));
+            } else {
+                int SHOW_LOG_LENGTH = Integer.parseInt(str[0]);
+                
+                if (file != null && file.exists()) {
+                    try{
+                        FileInputStream fis = new FileInputStream(file);
+                        FileChannel filechannel = fis.getChannel();
+                        size = filechannel.size();
+                        ByteBuffer bb;
+                        if (size <= SHOW_LOG_LENGTH) {
+                            bb = ByteBuffer.allocate((int) size);
+                            filechannel.read(bb, 0);
+                        } else {
+                            int pos = (int) (size - SHOW_LOG_LENGTH);
+                            bb = ByteBuffer.allocate(SHOW_LOG_LENGTH);
+                            filechannel.read(bb, pos);
+                        }
+                        bb.flip();
+                        String content = new String(bb.array()).replace("<", "&lt;")
+                        .replace(">", "&gt;").replace("\n", "<br/><br/>");
+                        buf.append("\r\ncontent:"+content);
+                        
+                        buf.append("\r\nmodified:"+(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+                        .format(new Date(file.lastModified()))));
+                        buf.append("\r\nsize:"+size +"\r\n");
+                    }catch (Exception e) {
+                        buf.append(e.getMessage());
+                    }
+                }else {
+                    size = 0;
+                    buf.append("\r\nMESSAGE: log file not exists or log appender is console .");
+                }
+            }
+        }
+        buf.append("\r\nCURRENT LOG LEVEL:"+ LoggerFactory.getLevel())
+        .append("\r\nCURRENT LOG APPENDER:"+ (file == null ? "console" : file.getAbsolutePath()));
+        return buf.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandler.java
new file mode 100644
index 0000000..62add42
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandler.java
@@ -0,0 +1,94 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import java.util.Collection;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * ServerTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "[-l] [port]", summary = "Print server ports and connections.", detail = "Print server ports and connections.")

+public class PortTelnetHandler implements TelnetHandler {

+

+    public String telnet(Channel channel, String message) {

+        StringBuilder buf = new StringBuilder();

+        String port = null;

+        boolean detail = false;

+        if (message.length() > 0) {

+            String[] parts = message.split("\\s+");

+            for (String part : parts) {

+                if ("-l".equals(part)) {

+                    detail = true;

+                } else {

+                    if (! StringUtils.isInteger(part)) {

+                        return "Illegal port " + part + ", must be integer.";

+                    }

+                    port = part;

+                }

+            }

+        }

+        if (port == null || port.length() == 0) {

+            for (ExchangeServer server : DubboProtocol.getDubboProtocol().getServers()) {

+                if (buf.length() > 0) {

+                    buf.append("\r\n");

+                }

+                if (detail) {

+                    buf.append(server.getUrl().getProtocol() + "://" + server.getUrl().getAddress());

+                } else {

+                    buf.append(server.getUrl().getPort());

+                }

+            }

+        } else {

+            int p = Integer.parseInt(port);

+            ExchangeServer server = null;

+            for (ExchangeServer s : DubboProtocol.getDubboProtocol().getServers()) {

+                if (p == s.getUrl().getPort()) {

+                    server = s;

+                    break;

+                }

+            }

+            if (server != null) {

+                Collection<ExchangeChannel> channels = server.getExchangeChannels();

+                for (ExchangeChannel c : channels) {

+                    if (buf.length() > 0) {

+                        buf.append("\r\n");

+                    }

+                    if (detail) {

+                        buf.append(c.getRemoteAddress() + " -> " + c.getLocalAddress());

+                    } else {

+                        buf.append(c.getRemoteAddress());

+                    }

+                }

+            } else {

+                buf.append("No such port " + port);

+            }

+        }

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.java
new file mode 100644
index 0000000..615438d
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.java
@@ -0,0 +1,92 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import java.lang.reflect.Method;

+

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.remoting.telnet.support.Help;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+import com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter;

+

+/**

+ * TraceTelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Activate

+@Help(parameter = "[service] [method] [times]", summary = "Trace the service.", detail = "Trace the service.")

+public class TraceTelnetHandler implements TelnetHandler {

+

+    public String telnet(Channel channel, String message) {

+        String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);

+        if ((service == null || service.length() == 0)

+                && (message == null || message.length() == 0)) {

+            return "Please input service name, eg: \r\ntrace XxxService\r\ntrace XxxService xxxMethod\r\ntrace XxxService xxxMethod 10\r\nor \"cd XxxService\" firstly.";

+        }

+        String[] parts = message.split("\\s+");

+        String method;

+        String times;

+        if (service == null || service.length() == 0) {

+            service = parts.length > 0 ? parts[0] : null;

+            method = parts.length > 1 ? parts[1] : null;

+        } else {

+            method = parts.length > 0 ? parts[0] : null;

+        }

+        if (StringUtils.isInteger(method)) {

+            times = method;

+            method = null;

+        } else {

+            times = parts.length > 2 ? parts[2] : "1";

+        }

+        if (! StringUtils.isInteger(times)) {

+            return "Illegal times " + times + ", must be integer.";

+        }

+        Invoker<?> invoker = null;

+        for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {

+            if (service.equals(exporter.getInvoker().getInterface().getSimpleName())

+                    || service.equals(exporter.getInvoker().getInterface().getName())

+                    || service.equals(exporter.getInvoker().getUrl().getPath())) {

+                invoker = exporter.getInvoker();

+                break;

+            }

+        }

+        if (invoker != null) {

+            if (method != null && method.length() > 0) {

+                boolean found = false;

+                for (Method m : invoker.getInterface().getMethods()) {

+                    if (m.getName().equals(method)) {

+                        found = true;

+                        break;

+                    }

+                }

+                if (! found) {

+                    return "No such method " + method + " in class " + invoker.getInterface().getName();

+                }

+            }

+            TraceFilter.addTracer(invoker.getInterface(), method, channel, Integer.parseInt(times));

+        } else {

+            return "No such service " + service;

+        }

+        return null;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..2729db5
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+server=com.alibaba.dubbo.rpc.protocol.dubbo.status.ServerStatusChecker

+threadpool=com.alibaba.dubbo.rpc.protocol.dubbo.status.ThreadPoolStatusChecker
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..63122b4
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,2 @@
+servers=com.alibaba.dubbo.rpc.protocol.dubbo.page.ServersPageHandler

+clients=com.alibaba.dubbo.rpc.protocol.dubbo.page.ClientsPageHandler
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec
new file mode 100644
index 0000000..3c6a1b4
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec
@@ -0,0 +1 @@
+dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler
new file mode 100644
index 0000000..780deef
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler
@@ -0,0 +1,7 @@
+ls=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ListTelnetHandler

+ps=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.PortTelnetHandler

+cd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler

+pwd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler

+invoke=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler

+trace=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler

+count=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..ff24401
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1,2 @@
+trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter

+future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..6725d68
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
new file mode 100644
index 0000000..4c21d1e
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc.protocol.dubbo;
+
+
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+/**
+ * 测试 dubboInvoker的Avilable状态
+ * @author chao.liuc
+ *
+ */
+public class DubboInvokerAvilableTest {
+    private static DubboProtocol     protocol = DubboProtocol.getDubboProtocol();
+    private static ProxyFactory proxy    = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() throws Exception {
+    }
+    
+    @Test
+    public void test_Normal_available(){
+        URL url = URL.valueOf("dubbo://127.0.0.1:20883/hi");
+        ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+        
+        DubboInvoker<?> invoker = (DubboInvoker<?>)protocol.refer(IDemoService.class, url);
+        Assert.assertEquals(true, invoker.isAvailable());
+        invoker.destroy();
+        Assert.assertEquals(false, invoker.isAvailable());
+    }
+    
+    @Test
+    public void test_Normal_ChannelReadOnly() throws Exception{
+        URL url = URL.valueOf("dubbo://127.0.0.1:20883/hi");
+        ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+        
+        DubboInvoker<?> invoker = (DubboInvoker<?>)protocol.refer(IDemoService.class, url);
+        Assert.assertEquals(true, invoker.isAvailable());
+        
+        getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
+        
+        Assert.assertEquals(false, invoker.isAvailable());
+     
+        //恢复状态,invoker共享连接
+        getClients(invoker)[0].removeAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY);
+    }
+    
+    @Test
+    public void test_NoInvokers() throws Exception{
+        URL url = URL.valueOf("dubbo://127.0.0.1:20883/hi?connections=1");
+        ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+        
+        DubboInvoker<?> invoker = (DubboInvoker<?>)protocol.refer(IDemoService.class, url);
+        
+        ExchangeClient[] clients = getClients(invoker);
+        clients[0].close();
+        Assert.assertEquals(false, invoker.isAvailable());
+        
+    }
+    
+    @Test
+    public void test_Lazy_ChannelReadOnly() throws Exception{
+        URL url = URL.valueOf("dubbo://127.0.0.1:20883/hi?lazy=true&connections=1");
+        ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+        
+        DubboInvoker<?> invoker = (DubboInvoker<?>)protocol.refer(IDemoService.class, url);
+        Assert.assertEquals(true, invoker.isAvailable());
+        
+        try{
+            getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
+            fail();
+        }catch (IllegalStateException e) {
+            
+        }
+        //invoke method --> init client
+        
+        IDemoService service = (IDemoService)proxy.getProxy(invoker);
+        Assert.assertEquals("ok", service.get());
+        
+        Assert.assertEquals(true, invoker.isAvailable());
+        getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
+        Assert.assertEquals(false, invoker.isAvailable());
+    }
+    
+    private ExchangeClient[] getClients(DubboInvoker<?> invoker) throws Exception{
+        Field field = DubboInvoker.class.getDeclaredField("clients");
+        field.setAccessible(true);
+        ExchangeClient[] clients = (ExchangeClient[])field.get(invoker);
+        Assert.assertEquals(1, clients.length);
+        return clients;
+    }
+    
+    public interface IDemoService{
+        public String get();
+    }
+    public class DemoServiceImpl implements IDemoService{
+        public String get(){
+            return "ok";
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java
new file mode 100644
index 0000000..723d6ed
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;

+
+/**
+ * dubbo protocol lazy connect test 
+ * @author chao.liuc
+ *
+ */
+public class DubboLazyConnectTest {
+    
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() throws Exception {
+    }
+    
+    @Test(expected = RpcException.class)
+    public void testSticky1(){
+        URL url = URL.valueOf("dubbo://127.0.0.1:9090/hi");
+        ProtocolUtils.refer(IDemoService.class, url);
+    }
+    
+    @Test
+    public void testSticky2(){
+        URL url = URL.valueOf("dubbo://127.0.0.1:9090/hi?"+Constants.LAZY_CONNECT_KEY+"=true");
+        ProtocolUtils.refer(IDemoService.class, url);
+    }
+    
+    @Test(expected = RpcException.class)
+    public void testSticky3() {
+        URL url = URL.valueOf("dubbo://127.0.0.1:9090/hi?"+Constants.LAZY_CONNECT_KEY+"=true");
+        IDemoService service = (IDemoService)ProtocolUtils.refer(IDemoService.class, url);
+        service.get();
+    }
+    
+    @Test
+    public void testSticky4() {
+        int port = NetUtils.getAvailablePort();
+        URL url = URL.valueOf("dubbo://127.0.0.1:"+port+"/hi?"+Constants.LAZY_CONNECT_KEY+"=true");
+        
+        ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+        
+        IDemoService service = (IDemoService)ProtocolUtils.refer(IDemoService.class, url);
+        Assert.assertEquals("ok", service.get());
+    }
+    
+    public interface IDemoService{
+        public String get();
+    }
+    public class DemoServiceImpl implements IDemoService{
+        public String get(){
+            return "ok";
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
new file mode 100644
index 0000000..118517a
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
@@ -0,0 +1,154 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+
+import static junit.framework.Assert.assertEquals;

+

+import java.util.HashMap;

+import java.util.Map;

+import java.util.Set;

+

+import junit.framework.Assert;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.NonSerialized;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.RemoteService;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.RemoteServiceImpl;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.Type;

+import com.alibaba.dubbo.rpc.service.EchoService;

+
+/**
+ * <code>ProxiesTest</code>
+ */
+
+public class DubboProtocolTest
+{
+    private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+    private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    
+	@Test
+	public void testDemoProtocol() throws Exception
+	{
+		DemoService service = new DemoServiceImpl();
+		protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange")));
+		service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange")));
+		assertEquals(service.getSize(new String[]{"", "", ""}), 3);
+	}
+
+	@Test
+	public void testDubboProtocol() throws Exception
+	{
+		DemoService service = new DemoServiceImpl();
+		protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName())));
+		service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName())));
+		assertEquals(service.enumlength(new Type[]{}), Type.Lower);
+		assertEquals(service.getSize(null), -1);
+		assertEquals(service.getSize(new String[]{"", "", ""}), 3);

+		Map<String, String> map = new HashMap<String, String>();

+		map.put("aa", "bb");

+		Set<String> set = service.keys(map);

+		assertEquals(set.size(), 1);

+		assertEquals(set.iterator().next(), "aa");
+		service.invoke("dubbo://127.0.0.1:9010/" + DemoService.class.getName() + "", "invoke");
+
+		service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName() + "?client=netty")));
+		// test netty client
+		StringBuffer buf = new StringBuffer();
+		for(int i=0;i<1024*32+32;i++)
+			buf.append('A');
+		System.out.println(service.stringLength(buf.toString()));
+
+		// cast to EchoService
+		EchoService echo = proxy.getProxy(protocol.refer(EchoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName() + "?client=netty")));
+		assertEquals(echo.$echo(buf.toString()), buf.toString());
+		assertEquals(echo.$echo("test"), "test");
+		assertEquals(echo.$echo("abcdefg"), "abcdefg");
+		assertEquals(echo.$echo(1234), 1234);
+	}
+

+    @Test

+    public void testDubboProtocolMultiService() throws Exception

+    {

+        DemoService service = new DemoServiceImpl();

+        protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName())));

+        service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName())));

+        

+        RemoteService remote = new RemoteServiceImpl();

+        protocol.export(proxy.getInvoker(remote, RemoteService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + RemoteService.class.getName())));

+        remote = proxy.getProxy(protocol.refer(RemoteService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + RemoteService.class.getName())));

+        

+        service.sayHello("world");

+        

+        // test netty client

+        assertEquals("world", service.echo("world"));

+        assertEquals("hello world@" + RemoteServiceImpl.class.getName(), remote.sayHello("world"));

+        

+        EchoService serviceEcho = (EchoService)service;

+        assertEquals(serviceEcho.$echo("test"), "test");

+        

+        EchoService remoteEecho = (EchoService)remote;

+        assertEquals(remoteEecho.$echo("ok"), "ok");

+    }

+
+	@Test
+	public void testPerm() throws Exception
+	{
+		DemoService service = new DemoServiceImpl();
+		protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));
+		service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));
+		long start = System.currentTimeMillis();
+		for(int i=0;i<1000;i++)
+			service.getSize(new String[]{"", "", ""});
+		System.out.println("take:"+(System.currentTimeMillis()-start));
+	}

+

+    @Test

+    public void testNonSerializedParameter() throws Exception

+    {

+        DemoService service = new DemoServiceImpl();

+        protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));

+        service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));

+        try {

+            service.nonSerializedParameter(new NonSerialized());

+            Assert.fail();

+        } catch (RpcException e) {

+            Assert.assertTrue(e.getMessage().contains("com.alibaba.dubbo.rpc.protocol.dubbo.support.NonSerialized must implement java.io.Serializable"));

+        }

+    }

+

+    @Test

+    public void testReturnNonSerialized() throws Exception

+    {

+        DemoService service = new DemoServiceImpl();

+        protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));

+        service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));

+        try {

+            service.returnNonSerialized();

+            Assert.fail();

+        } catch (RpcException e) {

+            Assert.assertTrue(e.getMessage().contains("com.alibaba.dubbo.rpc.protocol.dubbo.support.NonSerialized must implement java.io.Serializable"));

+        }

+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java
new file mode 100644
index 0000000..bc1a28b
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java
@@ -0,0 +1,359 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;

+
+public class ExplicitCallbackTest {
+    
+    protected Exporter<IDemoService> exporter = null;

+    protected Exporter<IHelloService> hello_exporter = null;
+    protected Invoker<IDemoService> reference = null;
+    
+    @After
+    public void tearDown(){
+        destroyService();
+    }
+    
+    public void exportService(){

+      //先export一个service,测试共享连接的问题

+        serviceURL=serviceURL.addParameter("connections", 1);

+        URL hellourl = serviceURL.setPath(IHelloService.class.getName());

+        hello_exporter = ProtocolUtils.export(new HelloServiceImpl(), IHelloService.class, hellourl);
+        exporter = ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, serviceURL);

+    }

+    void referService() {
+        demoProxy = (IDemoService)ProtocolUtils.refer(IDemoService.class, consumerUrl);
+    }
+
+    protected URL serviceURL = null ;
+    protected URL consumerUrl = null ;
+    
+    @Before
+    public void setUp(){
+    }
+    public void initOrResetUrl(int callbacks, int timeout) throws Exception {
+        int port = NetUtils.getAvailablePort() ;
+        consumerUrl = serviceURL =  URL.valueOf("dubbo://127.0.0.1:"+port+"/"+IDemoService.class.getName()+"?group=test" 
+                +"&xxx.0.callback=true"
+                +"&xxx2.0.callback=true"
+                +"&unxxx2.0.callback=false"
+                +"&timeout="+timeout

+                +"&retries=0"
+                +"&"+Constants.CALLBACK_INSTANCES_LIMIT_KEY+"="+callbacks
+                );
+        //      uncomment is unblock invoking
+//        serviceURL = serviceURL.addParameter("yyy."+Constants.ASYNC_KEY,String.valueOf(true));
+//        consumerUrl = consumerUrl.addParameter("yyy."+Constants.ASYNC_KEY,String.valueOf(true));
+    }
+    public void initOrResetBadUrl() throws Exception{
+        initOrResetUrl(1, 1000);
+        consumerUrl = serviceURL = serviceURL
+            .addParameter(Constants.DOWNSTREAM_CODEC_KEY, "dubbo1compatible")
+            ;  
+    }
+    public void initOrResetService(){
+        destroyService();
+        exportService();
+        referService();
+    }
+    public void destroyService(){
+        demoProxy = null ;
+        try {
+            if (exporter!=null) exporter.unexport();

+            if (hello_exporter!=null) hello_exporter.unexport();
+            if (reference!=null) reference.destroy();
+        }catch (Exception e) {
+        }
+    }
+    // ============================华丽的分割线================================================
+    interface IDemoCallback{
+        String yyy(String msg);
+    }

+    interface IHelloService{

+        public String sayHello();

+    }

+    
+    interface IDemoService{
+        public String get();
+        public int getCallbackCount();
+        public void xxx(IDemoCallback callback,String arg1,int runs,int sleep);
+        public void xxx2(IDemoCallback callback);
+        public void unxxx2(IDemoCallback callback);
+    }

+    

+    class HelloServiceImpl implements IHelloService{

+        public String sayHello() {

+            return "hello";

+        }

+        

+    }
+    class DemoServiceImpl  implements IDemoService {
+        public String get(){
+            return "ok" ;
+        }
+        public void xxx(final IDemoCallback callback ,String arg1, final int runs ,final int sleep) {
+            callback.yyy("Sync callback msg .This is callback data. arg1:"+arg1);
+            Thread t = new Thread(new Runnable() {
+                public void run() {
+                    for(int i = 0 ;i< runs ;i++){
+                        String ret = callback.yyy("server invoke callback : arg:"+System.currentTimeMillis());
+                        System.out.println("callback result is :"+ret);
+                        try {
+                            Thread.sleep(sleep);
+                        } catch (InterruptedException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+            });
+            t.setDaemon(true);
+            t.start();
+            System.out.println("xxx invoke complete");
+        }
+        
+        private List<IDemoCallback> callbacks = new ArrayList<IDemoCallback>();
+        public int getCallbackCount(){
+            return callbacks.size();
+        }
+        public void xxx2(IDemoCallback callback){
+            if (!callbacks.contains(callback)){
+                callbacks.add(callback);
+            }
+            startThread();
+        }
+        private volatile Thread t = null;
+        private volatile Lock lock = new ReentrantLock();
+        private void startThread(){
+            if (t == null || callbacks.size() == 0){
+                try{
+                    lock.lock();
+                    t = new Thread(new Runnable() {
+                        public void run() {
+                            while(callbacks.size()>0){
+                                try {
+                                    List<IDemoCallback> callbacksCopy = new ArrayList<IDemoCallback>(callbacks);
+                                    for(IDemoCallback callback : callbacksCopy){
+                                        try{
+                                            callback.yyy("this is callback msg,current time is :"+ System.currentTimeMillis());
+                                        }catch (Exception e) {
+                                            e.printStackTrace();
+                                            callbacks.remove(callback);
+                                        }
+                                    }
+                                    Thread.sleep(100);
+                                } catch (InterruptedException e) {
+                                    e.printStackTrace();
+                                }
+                            }
+                        }
+                    });
+                    t.setDaemon(true);
+                    t.start(); 
+                }finally{
+                    lock.unlock();
+                }
+            }
+        }
+        
+        public void unxxx2(IDemoCallback callback) {
+            if (!callbacks.contains(callback)){
+                throw new IllegalStateException("callback instance not found");
+            }
+            callbacks.remove(callback);
+        }
+    }
+    
+    
+    // ============================华丽的分割线================================================
+    IDemoService demoProxy = null; 
+    @Test
+    public void TestCallbackNormal() throws Exception {

+        

+        initOrResetUrl(1, 10000000); initOrResetService() ;
+        final AtomicInteger count = new AtomicInteger(0);
+        
+        demoProxy.xxx(new IDemoCallback() {
+            public String yyy(String msg) {
+                System.out.println("Recived callback: " + msg);
+                count.incrementAndGet();
+                return "ok";
+            }
+        },"other custom args" , 10 , 100);
+        System.out.println("Async...");

+//        Thread.sleep(10000000);
+        assertCallbackCount(10,100,count);
+        destroyService();

+        

+        
+    }
+    
+    @Test
+    public void TestCallbackMultiInstans() throws Exception {
+        initOrResetUrl(2, 1000);
+        initOrResetService() ;
+        IDemoCallback callback = new IDemoCallback(){
+            public String yyy(String msg) {
+                System.out.println("callback1:"+msg);
+                return "callback1 onChanged ,"+msg;
+            }
+        };
+        
+        IDemoCallback callback2 = new IDemoCallback(){
+            public String yyy(String msg) {
+                System.out.println("callback2:"+msg);
+                return "callback2 onChanged ,"+msg;
+            }
+        };
+        {    
+            demoProxy.xxx2(callback);
+            Assert.assertEquals(1, demoProxy.getCallbackCount());
+            Thread.sleep(500);
+            demoProxy.unxxx2(callback);
+            Assert.assertEquals(0, demoProxy.getCallbackCount());
+            System.out.println("");
+            
+            demoProxy.xxx2(callback2);
+            Assert.assertEquals(1, demoProxy.getCallbackCount());
+            Thread.sleep(500);
+            demoProxy.unxxx2(callback2);
+            Assert.assertEquals(0, demoProxy.getCallbackCount());
+            System.out.println("");
+            demoProxy.xxx2(callback);
+            Thread.sleep(500);
+            Assert.assertEquals(1, demoProxy.getCallbackCount());
+            demoProxy.unxxx2(callback);
+            Assert.assertEquals(0, demoProxy.getCallbackCount());
+        }
+        {
+            demoProxy.xxx2(callback);
+            Assert.assertEquals(1, demoProxy.getCallbackCount());
+            
+            demoProxy.xxx2(callback);
+            Assert.assertEquals(1, demoProxy.getCallbackCount());
+            
+            demoProxy.xxx2(callback2);
+            Assert.assertEquals(2, demoProxy.getCallbackCount());
+        }
+        destroyService();
+    }
+    
+//    @Ignore
+    @Test(expected=RpcException.class)
+    public void TestCallbackDownStreamCodec() throws Exception {
+        initOrResetBadUrl(); initOrResetService() ;
+        final AtomicInteger count = new AtomicInteger(0);
+        demoProxy.xxx(new IDemoCallback() {
+            public String yyy(String msg) {
+                System.out.println("Recived callback: " + msg);
+                count.incrementAndGet();
+                return "ok";
+            }
+        },"other custom args" , 10 , 100);
+        System.out.println("Async...");
+        assertCallbackCount(10,100,count);
+        destroyService();
+    }
+    
+    @Test(expected = RpcException.class)
+    public void TestCallbackConsumerLimit() throws Exception {
+        initOrResetUrl(1, 1000);
+        //api的方式 url 无法自动从服务端传递到客户端,需要手动制定
+        initOrResetService() ;
+        final AtomicInteger count = new AtomicInteger(0);
+        demoProxy.xxx(new IDemoCallback() {
+            public String yyy(String msg) {
+                System.out.println("Recived callback: " + msg);
+                count.incrementAndGet();
+                return "ok";
+            }
+        },"other custom args" , 10 , 100);
+        
+        demoProxy.xxx(new IDemoCallback() {
+            public String yyy(String msg) {
+                System.out.println("Recived callback: " + msg);
+                count.incrementAndGet();
+                return "ok";
+            }
+        },"other custom args" , 10 , 100);
+        destroyService();
+    }
+    
+    @Test(expected = RpcException.class)
+    public void TestCallbackProviderLimit() throws Exception {
+        initOrResetUrl(1, 1000);
+        //api的方式 url 无法自动从服务端传递到客户端,需要手动制定
+        serviceURL = serviceURL.addParameter(Constants.CALLBACK_INSTANCES_LIMIT_KEY, 1+"");
+        initOrResetService() ;
+        final AtomicInteger count = new AtomicInteger(0);
+        demoProxy.xxx(new IDemoCallback() {
+            public String yyy(String msg) {
+                System.out.println("Recived callback: " + msg);
+                count.incrementAndGet();
+                return "ok";
+            }
+        },"other custom args" , 10 , 100);
+        
+        demoProxy.xxx(new IDemoCallback() {
+            public String yyy(String msg) {
+                System.out.println("Recived callback: " + msg);
+                count.incrementAndGet();
+                return "ok";
+            }
+        },"other custom args" , 10 , 100);
+        destroyService();
+    }
+    
+    private void assertCallbackCount(int runs, int sleep, AtomicInteger count) throws InterruptedException{
+        int last = count.get();
+        for(int i=0;i< runs  ;i++){
+            if (last > runs) break;
+            Thread.sleep(sleep * 2);
+            System.out.println(count.get() + "  " + last);
+            Assert.assertTrue(count.get() > last);
+            last = count.get();
+        }
+        //有一次同步调用callback
+        Assert.assertEquals(runs+1, count.get());
+    }
+    
+    @Ignore //使用不同进程启动
+    @Test
+    public void startProvider() throws Exception {
+        exportService();
+        synchronized (ExplicitCallbackTest.class) {
+            ExplicitCallbackTest.class.wait();
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureFilterTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
new file mode 100644
index 0000000..bf5043d
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
@@ -0,0 +1,91 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;

+

+import static org.junit.Assert.assertEquals;

+

+import org.easymock.EasyMock;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;

+

+/**

+ * EventFilterTest.java

+ * 

+ * @author tony.chenl

+ * TODO 暂时依赖callback集成测试,后续补充

+ */

+public class FutureFilterTest {

+    Filter                    eventFilter = new FutureFilter();

+    private static Invocation invocation;

+

+    @BeforeClass

+    public static void setUp() {

+        invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();

+        EasyMock.replay(invocation);

+    }

+

+    @Test

+    public void testSyncCallback() {

+        @SuppressWarnings("unchecked")

+        Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setValue("High");

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        Result filterResult = eventFilter.invoke(invoker, invocation);

+        assertEquals("High", filterResult.getValue());

+    }

+

+    @Test(expected = RuntimeException.class)

+    public void testSyncCallbackHasException() throws RpcException, Throwable {

+        Invocation invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();

+        EasyMock.replay(invocation);

+        @SuppressWarnings("unchecked")

+        Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setException(new RuntimeException());

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&"+Constants.ON_THROW_METHOD_KEY+"=echo");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        eventFilter.invoke(invoker, invocation).recreate();

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java
new file mode 100644
index 0000000..936e582
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java
@@ -0,0 +1,373 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+
+import java.io.Serializable;

+import java.lang.reflect.Method;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.Future;

+import java.util.concurrent.TimeUnit;

+

+import junit.framework.Assert;

+

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.StaticContext;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;

+
+public class ImplicitCallBackTest{
+
+    protected Exporter<IDemoService> exporter = null;
+    protected Invoker<IDemoService> reference = null;
+    
+    protected URL serviceURL = null ;
+    protected URL consumerUrl = null ;
+    Method onReturnMethod;
+    Method onThrowMethod ;
+    Method onInvokeMethod ;
+    
+    @Before
+    public void setUp() throws SecurityException, NoSuchMethodException{
+        onReturnMethod = Nofify.class.getMethod("onreturn", new Class<?>[]{Person.class, Integer.class});
+        onThrowMethod = Nofify.class.getMethod("onthrow", new Class<?>[]{Throwable.class, Integer.class});
+        onInvokeMethod = Nofify.class.getMethod("oninvoke", new Class<?>[]{Integer.class});
+    }

+    

+    @After

+    public void tearDown(){

+        ProtocolUtils.closeAll();

+    }
+    
+    public void initOrResetService(){
+        destroyService();
+        exportService();
+        referService();
+    }
+    public void destroyService(){
+        demoProxy = null ;
+        try {
+            if (exporter!=null) exporter.unexport();
+            if (reference!=null) reference.destroy();
+        }catch (Exception e) {
+        }
+    }
+    
+    void referService() {
+        demoProxy = (IDemoService)ProtocolUtils.refer(IDemoService.class, consumerUrl);
+    }
+    
+    public void exportService(){
+        exporter = ProtocolUtils.export(new NormalDemoService(), IDemoService.class, serviceURL);
+    }
+    public void exportExService(){
+        exporter = ProtocolUtils.export(new ExceptionDemoExService(), IDemoService.class, serviceURL);
+    }
+    
+    public void initOrResetUrl(boolean isAsync) throws Exception {
+        int port = NetUtils.getAvailablePort() ;
+        consumerUrl = serviceURL =  URL.valueOf("dubbo://127.0.0.1:"+port+"/"+IDemoService.class.getName()+"?group=test&async="+isAsync+"&timeout=100000&reference.filter=future" );
+        StaticContext.getSystemContext().clear();
+    }
+    
+    public void initImplicitCallBackURL_onlyOnthrow() throws Exception {
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_THROW_METHOD_KEY),onThrowMethod);
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_THROW_INSTANCE_KEY),notify);
+    }
+    public void initImplicitCallBackURL_onlyOnreturn() throws Exception {
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_RETURN_METHOD_KEY),onReturnMethod);
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_RETURN_INSTANCE_KEY),notify);
+        
+    }
+    public void initImplicitCallBackURL_onlyOninvoke() throws Exception {
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_INVOKE_METHOD_KEY),onInvokeMethod);
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_INVOKE_INSTANCE_KEY),notify);
+    }
+    
+    //================================================================================================
+    
+    NofifyImpl notify = new NofifyImpl();
+    
+    interface Nofify {
+        public void onreturn(Person msg, Integer id);
+        public void onthrow(Throwable ex, Integer id);
+        public void oninvoke(Integer id);
+    }
+    class NofifyImpl implements Nofify{
+        public List<Integer> inv = new ArrayList<Integer> ();
+        public Map<Integer ,Person> ret = new HashMap<Integer ,Person> ();
+        public Map<Integer ,Throwable> errors = new HashMap<Integer ,Throwable> ();
+        public boolean exd = false; 
+        public void onreturn(Person msg, Integer id) {
+            System.out.println("onNotify:"+msg);
+            ret.put(id, msg);
+        }
+        public void onthrow(Throwable ex, Integer id) {
+            errors.put(id, ex);
+//            ex.printStackTrace();
+        }
+        public void oninvoke(Integer id) {
+            inv.add(id);
+        }
+    }
+    
+    interface IDemoService{
+        public Person get(int id);
+    }
+    
+    class NormalDemoService  implements IDemoService {
+        public Person get(int id){
+            return new Person(id, "charles", 4);
+        }
+    }
+    class ExceptionDemoExService  implements IDemoService {
+        public Person get(int id){
+            throw new RuntimeException("request persion id is :"+ id);
+        }
+    }
+
+    public static class Person implements Serializable{
+        private static final long serialVersionUID = 1L;
+        public Person(int id, String name, int age) {
+            this.id = id;
+            this.name = name;
+            this.age = age;
+        }
+        private int id;
+        private String name ;
+        private int age;
+        public String getName() {
+            return name;
+        }
+        public void setName(String name) {
+            this.name = name;
+        }
+        public int getAge() {
+            return age;
+        }
+        public void setAge(int age) {
+            this.age = age;
+        }
+        public int getId() {
+            return id;
+        }
+        public void setId(int id) {
+            this.id = id;
+        }
+        @Override
+        public String toString() {
+            return "Person [name=" + name + ", age=" + age + "]";
+        }
+    }
+    //================================================================================================
+    IDemoService demoProxy = null;
+    
+    @Test
+    public void test_CloseCallback() throws Exception {
+        initOrResetUrl(false);
+        initOrResetService() ;
+        Person ret = demoProxy.get(1);
+        Assert.assertEquals(1, ret.getId());
+        destroyService();
+    }
+    
+    @Test
+    public void test_Sync_Onreturn() throws Exception {
+        initOrResetUrl(false);
+        initImplicitCallBackURL_onlyOnreturn();
+        initOrResetService() ;
+        int requestId = 2;
+        Person ret = demoProxy.get(requestId);
+        Assert.assertEquals(requestId, ret.getId());
+        for (int i = 0; i < 10; i++) {
+            if (! notify.ret.containsKey(requestId)){
+                Thread.sleep(200);
+            }else {
+                break;
+            }
+        }
+        Assert.assertEquals(requestId, notify.ret.get(requestId).getId());
+        destroyService();
+    }
+    
+    @Test
+    public void test_Ex_OnReturn() throws Exception {
+        initOrResetUrl(true);
+        initImplicitCallBackURL_onlyOnreturn();
+        
+        destroyService();
+        exportExService();
+        referService();
+        
+        int requestId = 2;
+        Person ret = demoProxy.get(requestId);
+        Assert.assertEquals(null, ret);
+        for (int i = 0; i < 10; i++) {
+            if (! notify.errors.containsKey(requestId)){
+                Thread.sleep(200);
+            }else {
+                break;
+            }
+        }
+        Assert.assertTrue(! notify.errors.containsKey(requestId));
+        destroyService();
+    }
+    
+    @Test
+    public void test_Ex_OnInvoke() throws Exception {
+        initOrResetUrl(true);
+        initImplicitCallBackURL_onlyOninvoke();
+        
+        destroyService();
+        exportExService();
+        referService();
+        
+        int requestId = 2;
+        Person ret = demoProxy.get(requestId);
+        Assert.assertEquals(null, ret);
+        for (int i = 0; i < 10; i++) {
+            if (! notify.inv.contains(requestId)){
+                Thread.sleep(200);
+            }else {
+                break;
+            }
+        }
+        Assert.assertTrue(notify.inv.contains(requestId));
+        destroyService();
+    }
+    
+    @Test
+    public void test_Ex_Onthrow() throws Exception {
+        initOrResetUrl(true);
+        initImplicitCallBackURL_onlyOnthrow();
+        
+        destroyService();
+        exportExService();
+        referService();
+        
+        int requestId = 2;
+        Person ret = demoProxy.get(requestId);
+        Assert.assertEquals(null, ret);
+        for (int i = 0; i < 10; i++) {
+            if (! notify.errors.containsKey(requestId)){
+                Thread.sleep(200);
+            }else {
+                break;
+            }
+        }
+        Assert.assertTrue(notify.errors.containsKey(requestId));
+        Assert.assertTrue(notify.errors.get(requestId) instanceof Throwable);
+        destroyService();
+    }
+    
+    @Test
+    public void test_Sync_NoFuture() throws Exception {
+        initOrResetUrl(false);
+        initImplicitCallBackURL_onlyOnreturn();
+        destroyService();
+        exportService();
+        referService();
+        
+        int requestId = 2;
+        Person ret = demoProxy.get(requestId);
+        Assert.assertEquals(requestId, ret.getId());
+        Future<Person> pFuture = RpcContext.getContext().getFuture();
+        Assert.assertEquals(null, pFuture);
+        destroyService();
+    }
+    
+    @Test
+    public void test_Async_Future() throws Exception {
+        initOrResetUrl(true);
+        destroyService();
+        exportService();
+        referService();
+        
+        int requestId = 2;
+        Person ret = demoProxy.get(requestId);
+        Assert.assertEquals(null, ret);
+        Future<Person> pFuture = RpcContext.getContext().getFuture();
+        ret = pFuture.get(1000, TimeUnit.MICROSECONDS);
+        Assert.assertEquals(requestId, ret.getId());
+        destroyService();
+    }
+    
+    @Test
+    public void test_Async_Future_Multi() throws Exception {
+        initOrResetUrl(true);
+        destroyService();
+        exportService();
+        referService();
+        
+        int requestId1 = 1;
+        Person ret = demoProxy.get(requestId1);
+        Assert.assertEquals(null, ret);
+        Future<Person> p1Future = RpcContext.getContext().getFuture();
+        
+        int requestId2 = 1;
+        Person ret2 = demoProxy.get(requestId2);
+        Assert.assertEquals(null, ret2);
+        Future<Person> p2Future = RpcContext.getContext().getFuture();
+        
+        ret = p1Future.get(1000, TimeUnit.MICROSECONDS);
+        ret2 = p2Future.get(1000, TimeUnit.MICROSECONDS);
+        Assert.assertEquals(requestId1, ret.getId());
+        Assert.assertEquals(requestId2, ret.getId());
+        destroyService();
+    }
+    
+    @Test(expected = RuntimeException.class)
+    public void test_Async_Future_Ex() throws Exception {
+        try{
+            initOrResetUrl(true);
+            destroyService();
+            exportExService();
+            referService();
+            
+            int requestId = 2;
+            Person ret = demoProxy.get(requestId);
+            Assert.assertEquals(null, ret);
+            Future<Person> pFuture = RpcContext.getContext().getFuture();
+            ret = pFuture.get(1000, TimeUnit.MICROSECONDS);
+            Assert.assertEquals(requestId, ret.getId());
+        }finally{
+            destroyService();
+        }
+    }
+    
+    @Test(expected = RuntimeException.class)
+    public void test_Normal_Ex() throws Exception {
+        initOrResetUrl(false);
+        destroyService();
+        exportExService();
+        referService();
+        
+        int requestId = 2;
+        Person ret = demoProxy.get(requestId);
+        Assert.assertEquals(requestId, ret.getId());
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/MultiThreadTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/MultiThreadTest.java
new file mode 100644
index 0000000..5ca2070
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/MultiThreadTest.java
@@ -0,0 +1,71 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.TestCase;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl;

+
+public class MultiThreadTest extends TestCase
+{
+    
+    private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+    private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    
+	public void testDubboMultiThreadInvoke() throws Exception
+	{
+	    Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, URL.valueOf("dubbo://127.0.0.1:20880/TestService")));
+		
+		final AtomicInteger counter = new AtomicInteger();
+		final DemoService service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:20880/TestService")));
+		assertEquals(service.getSize(new String[]{"123", "456", "789"}), 3);
+
+		final StringBuffer sb = new StringBuffer();
+		for(int i=0;i<1024*64+32;i++)
+			sb.append('A');
+		assertEquals(sb.toString(), service.echo(sb.toString()));
+
+		ExecutorService exec = Executors.newFixedThreadPool(10);
+		for(int i=0;i<10;i++)
+		{
+			final int fi = i;
+			exec.execute(new Runnable(){
+				public void run()
+				{
+					for(int i=0;i<30;i++)
+					{
+						System.out.println(fi+":"+counter.getAndIncrement());
+						assertEquals(service.echo(sb.toString()), sb.toString());
+					}
+				}
+			});
+		}
+		exec.shutdown();
+		exec.awaitTermination(10, TimeUnit.SECONDS);
+		rpcExporter.unexport();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java
new file mode 100644
index 0000000..4dee1b4
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java
@@ -0,0 +1,229 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import java.lang.reflect.Field;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.DubboAppender;
+import com.alibaba.dubbo.common.utils.LogUtil;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+public class ReferenceCountExchangeClientTest {
+    
+    Exporter<?> demoExporter ;
+    Exporter<?> helloExporter ;
+    
+    Invoker<IDemoService> demoServiceInvoker;
+    Invoker<IHelloService> helloServiceInvoker;
+    
+    IDemoService demoService ;
+    IHelloService helloService;
+    
+    ExchangeClient demoClient ;
+    ExchangeClient helloClient ;
+    
+    String errorMsg = "safe guard client , should not be called ,must have a bug";
+    
+    public interface IDemoService{
+        public String demo();
+    }
+    public class DemoServiceImpl implements IDemoService{
+        public String demo(){
+            return "demo";
+        }
+    }
+    public interface IHelloService{
+        public String hello();
+    }
+    public class HelloServiceImpl implements IHelloService{
+        public String hello(){
+            return "hello";
+        }
+    }
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    /**
+     * 测试共享连接
+     */
+    @Test
+    public void test_share_connect(){
+        init(0);
+        Assert.assertEquals(demoClient.getLocalAddress(), helloClient.getLocalAddress());
+        Assert.assertEquals(demoClient, helloClient);
+        destoy();
+    }
+    
+    /**
+     * 测试不共享连接
+     */
+    @Test
+    public void test_not_share_connect(){
+        init(1);
+        Assert.assertNotSame(demoClient.getLocalAddress(), helloClient.getLocalAddress());
+        Assert.assertNotSame(demoClient, helloClient);
+        destoy();
+    }
+    
+    /**
+     * 测试invoker多次destory不会导致计数器多次减少
+     */
+    @Test
+    public void test_multi_destory(){
+        init(0);
+        DubboAppender.doStart();
+        DubboAppender.clear();
+        demoServiceInvoker.destroy();
+        demoServiceInvoker.destroy();
+        Assert.assertEquals("hello", helloService.hello());
+        Assert.assertEquals("should not  warning message", 0, LogUtil.findMessage(errorMsg));
+        LogUtil.checkNoError();
+        DubboAppender.doStop();
+        destoy();
+    }
+    
+    /**
+     * 测试计数器错误,调用成功
+     */
+    @Test
+    public void test_counter_error(){
+        init(0);
+        DubboAppender.doStart();
+        DubboAppender.clear();
+        
+        ReferenceCountExchangeClient client = getReferenceClient(helloServiceInvoker);
+        //close一次,计数器从2减少到1,不能warning
+        client.close();
+        Assert.assertEquals("hello", helloService.hello());
+        Assert.assertEquals("should not warning message", 0, LogUtil.findMessage(errorMsg));
+        //计数器错误,调用正常
+        client.close();
+        Assert.assertEquals("hello", helloService.hello());
+        Assert.assertEquals("should warning message", 1, LogUtil.findMessage(errorMsg));
+       
+        //调用5千次输出一个错误
+        Assert.assertEquals("hello", helloService.hello());
+        Assert.assertEquals("should warning message", 1, LogUtil.findMessage(errorMsg));
+        
+        DubboAppender.doStop();
+        
+        //重新调用一次后status已经是available.
+        Assert.assertEquals("client status available", true, helloServiceInvoker.isAvailable());
+        
+        client.close();
+        //client已经被替换为lazyclient lazy client从referenceclientmap中获取,获取到的是上次的client(已经被调用过一次),所以close状态为false
+        Assert.assertEquals("client status close", false, client.isClosed());
+        Assert.assertEquals("client status close", false, helloServiceInvoker.isAvailable());
+        destoy();
+    }
+    
+    @SuppressWarnings("unchecked")
+    private void init(int connections){
+        int port = NetUtils.getAvailablePort();
+        URL demoUrl = URL.valueOf("dubbo://127.0.0.1:"+port+"/demo?"+Constants.CONNECTIONS_KEY+"="+connections);
+        URL helloUrl = URL.valueOf("dubbo://127.0.0.1:"+port+"/hello?"+Constants.CONNECTIONS_KEY+"="+connections);
+        
+        demoExporter = export(new DemoServiceImpl(), IDemoService.class, demoUrl);
+        helloExporter = export(new HelloServiceImpl(), IHelloService.class, helloUrl);
+        
+        demoServiceInvoker = (Invoker<IDemoService>) referInvoker(IDemoService.class, demoUrl);
+        demoService = proxy.getProxy(demoServiceInvoker);
+        Assert.assertEquals("demo", demoService.demo());
+        
+        helloServiceInvoker = (Invoker<IHelloService>) referInvoker(IHelloService.class, helloUrl);
+        helloService = proxy.getProxy(helloServiceInvoker);
+        Assert.assertEquals("hello", helloService.hello());
+        
+        demoClient = getClient(demoServiceInvoker);
+        helloClient = getClient(helloServiceInvoker);
+    }
+    
+    private void destoy(){
+        demoServiceInvoker.destroy();
+        helloServiceInvoker.destroy();
+        demoExporter.getInvoker().destroy();
+        helloExporter.getInvoker().destroy();
+    }
+    
+    private ExchangeClient getClient(Invoker<?> invoker){
+        if (invoker.getUrl().getParameter(Constants.CONNECTIONS_KEY, 1) == 1){
+            return getInvokerClient(invoker);
+        } else {
+            ReferenceCountExchangeClient client = getReferenceClient(invoker);
+            try {
+                Field clientField = ReferenceCountExchangeClient.class.getDeclaredField("client");
+                clientField.setAccessible(true);
+                return (ExchangeClient) clientField.get(client);
+            } catch (Exception e) {
+                e.printStackTrace();
+                Assert.fail(e.getMessage());
+                throw new RuntimeException(e);
+            }
+        }
+    }
+    
+    private ReferenceCountExchangeClient getReferenceClient(Invoker<?> invoker){
+        return (ReferenceCountExchangeClient)getInvokerClient(invoker);
+    }
+    
+    private ExchangeClient getInvokerClient(Invoker<?> invoker){
+        @SuppressWarnings("rawtypes")
+        DubboInvoker dInvoker = (DubboInvoker)invoker;
+        try {
+            Field clientField = DubboInvoker.class.getDeclaredField("clients");
+            clientField.setAccessible(true);
+            ExchangeClient[] clients = (ExchangeClient[]) clientField.get(dInvoker);
+            return clients[0];
+            
+        } catch (Exception e) {
+            e.printStackTrace();
+            Assert.fail(e.getMessage());
+            throw new RuntimeException(e);
+        }
+    }
+    
+    private static DubboProtocol     protocol = DubboProtocol.getDubboProtocol();
+    public static  ProxyFactory proxy    = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    public static Invoker<?> referInvoker(Class<?> type, URL url) {
+        return (Invoker<?>)protocol.refer(type, url);
+    }
+
+    public static <T> Exporter<T> export(T instance, Class<T> type, String url) {
+        return export(instance, type, URL.valueOf(url));
+    }
+    
+    public static <T> Exporter<T> export(T instance, Class<T> type, URL url) {
+        return protocol.export(proxy.getInvoker(instance, type, url));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/RpcFilterTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/RpcFilterTest.java
new file mode 100644
index 0000000..fbd4de9
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/RpcFilterTest.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo;
+
+import junit.framework.TestCase;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl;

+import com.alibaba.dubbo.rpc.service.EchoService;
+
+public class RpcFilterTest extends TestCase
+{
+    private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+    private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    
+	public void testRpcFilter() throws Exception
+	{
+		DemoService service = new DemoServiceImpl();
+		URL url = URL.valueOf("dubbo://127.0.0.1:9010/com.alibaba.dubbo.rpc.DemoService?service.filter=echo");
+		protocol.export(proxy.getInvoker(service, DemoService.class, url));
+		service = proxy.getProxy(protocol.refer(DemoService.class, url));
+		assertEquals("123",service.echo("123"));
+		// cast to EchoService
+		EchoService echo = proxy.getProxy(protocol.refer(EchoService.class, url));
+		assertEquals(echo.$echo("test"), "test");
+		assertEquals(echo.$echo("abcdefg"), "abcdefg");
+		assertEquals(echo.$echo(1234), 1234);
+	}
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/CustomArgument.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/CustomArgument.java
new file mode 100644
index 0000000..0aead9e
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/CustomArgument.java
@@ -0,0 +1,49 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;
+
+import java.io.Serializable;
+

+
+/**
+ * @author chao.liuc
+ *
+ */
+@SuppressWarnings("serial")
+public class CustomArgument implements Serializable{
+    
+    public CustomArgument(){}
+    
+    public CustomArgument(Type type, String name) {
+        super();
+        this.type = type;
+        this.name = name;
+    }
+    Type type;
+    String name;
+    public Type getType() {
+        return type;
+    }
+    public void setType(Type type) {
+        this.type = type;
+    }
+    public String getName() {
+        return name;
+    }
+    public void setName(String name) {
+        this.name = name;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoRequest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoRequest.java
new file mode 100644
index 0000000..66c6892
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoRequest.java
@@ -0,0 +1,64 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;
+
+import java.io.Serializable;
+
+/**
+ * TestRequest.
+ * 
+ * @author qian.lei
+ */
+
+class DemoRequest implements Serializable
+{
+	private static final long serialVersionUID = -2579095288792344869L;
+
+	private String mServiceName;
+	
+	private String mMethodName;
+
+	private Class<?>[] mParameterTypes;
+
+	private Object[] mArguments;
+
+	public DemoRequest(String serviceName,String methodName, Class<?>[] parameterTypes,Object[] args)
+	{
+		mServiceName = serviceName;
+		mMethodName = methodName;
+		mParameterTypes = parameterTypes;
+		mArguments = args;
+	}
+
+	public String getServiceName()
+	{
+		return mServiceName;
+	}
+
+	public String getMethodName()
+	{
+		return mMethodName;
+	}
+
+	public Class<?>[] getParameterTypes() {
+		return mParameterTypes;
+	}
+
+	public Object[] getArguments()
+	{
+		return mArguments;
+	}
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoService.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoService.java
new file mode 100644
index 0000000..2619e97
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoService.java
@@ -0,0 +1,59 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;
+

+import java.util.Map;

+import java.util.Set;

+

+

+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+	void sayHello(String name);

+	

+	Set<String> keys(Map<String, String> map);
+
+	String echo(String text);
+
+	long timestamp();
+
+	String getThreadName();
+
+	int getSize(String[] strs);
+
+	int getSize(Object[] os);
+
+	Object invoke(String service, String method) throws Exception;
+
+	int stringLength(String str);
+
+	Type enumlength(Type... types);
+	
+//	Type enumlength(Type type);
+	
+	String get(CustomArgument arg1);
+	
+	byte getbyte(byte arg);

+	

+	void nonSerializedParameter(NonSerialized ns);

+	

+	NonSerialized returnNonSerialized();

+	
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java
new file mode 100644
index 0000000..9eef1df
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java
@@ -0,0 +1,110 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;
+

+import java.util.Map;

+import java.util.Set;

+

+import com.alibaba.dubbo.rpc.RpcContext;

+
+/**
+ * DemoServiceImpl
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+	public DemoServiceImpl()
+	{
+		super();
+	}
+
+	public void sayHello(String name) {
+		System.out.println("hello "+name);
+	}
+
+	public String echo(String text)
+	{
+		return text;
+	}
+
+	public long timestamp() {
+		return System.currentTimeMillis();
+	}
+
+	public String getThreadName()
+	{
+		return Thread.currentThread().getName();
+	}
+
+	public int getSize(String[] strs)
+	{
+		if( strs == null )
+			return -1;
+		return strs.length;
+	}
+
+	public int getSize(Object[] os)
+	{
+		if( os == null )
+			return -1;
+		return os.length;
+	}
+
+	public Object invoke(String service, String method) throws Exception
+	{
+		System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+		return service + ":" + method;
+	}
+
+	public Type enumlength(Type... types)
+	{
+		if( types.length == 0 )
+			return Type.Lower;
+		return types[0];
+	}
+	
+	public Type enumlength(Type type)
+    {
+       return type;
+    }
+
+	public int stringLength(String str)
+	{
+		return str.length();
+	}
+	public String get(CustomArgument arg1){
+	    return arg1.toString();
+	}
+
+    public byte getbyte(byte arg) {
+        return arg;
+    }

+

+    public Person gerPerson(Person person) {

+        return person;

+    }

+

+    public Set<String> keys(Map<String, String> map) {

+        return map == null ? null : map.keySet();

+    }

+

+    public void nonSerializedParameter(NonSerialized ns) {

+    }

+

+    public NonSerialized returnNonSerialized() {

+        return new NonSerialized();

+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/EnumBak.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/EnumBak.java
new file mode 100644
index 0000000..478aa37b
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/EnumBak.java
@@ -0,0 +1,183 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;
+
+
+import java.util.HashMap;

+import java.util.Map;

+

+import org.junit.Assert;

+import org.junit.Ignore;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.service.GenericService;

+
+public class EnumBak {
+    
+    private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+    private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    
+    @Test
+    public void testNormal(){
+        int port = NetUtils.getAvailablePort();
+        URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?proxy=jdk" 
+                + "&interface=" + DemoService.class.getName()
+        		+ "&timeout=" + Integer.MAX_VALUE
+                );
+        DemoService demo = new DemoServiceImpl();
+        Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+        protocol.export(invoker);
+        
+        URL consumerurl = serviceurl;
+        Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+        DemoService demoProxy = (DemoService)proxy.getProxy(reference);
+//        System.out.println(demoProxy.getThreadName());
+        Assert.assertEquals((byte)-128, demoProxy.getbyte((byte)-128));
+        
+//        invoker.destroy();
+        reference.destroy();
+    }
+    @Ignore
+    @Test
+    public void testExportService() throws InterruptedException{
+        int port = NetUtils.getAvailablePort();
+        URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?proxy=jdk&timeout="+Integer.MAX_VALUE
+                );
+        DemoService demo = new DemoServiceImpl();
+        Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+        protocol.export(invoker);
+        synchronized (EnumBak.class) {
+            EnumBak.class.wait();
+        }
+        
+//        URL consumerurl = serviceurl;
+//        Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+//        DemoService demoProxy = (DemoService)proxyFactory.createProxy(reference);
+////        System.out.println(demoProxy.getThreadName());
+//        System.out.println("byte:"+demoProxy.getbyte((byte)-128));
+//        
+//        invoker.destroy();
+//        reference.destroy();
+    }
+
+    @Test
+    public void testNormalEnum(){
+        int port = NetUtils.getAvailablePort();
+        URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+                );
+        DemoService demo = new DemoServiceImpl();
+        Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+        protocol.export(invoker);
+        
+        URL consumerurl = serviceurl;
+        Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+        DemoService demoProxy = (DemoService)proxy.getProxy(reference);
+        Type type = demoProxy.enumlength(Type.High);
+        System.out.println(type);
+        Assert.assertEquals(Type.High, type);
+        
+        invoker.destroy();
+        reference.destroy();
+    }
+//    @Test
+    //测试2.0.5调用2.0.3的兼容
+    public void testEnumCompat(){
+        int port = 20880;
+        URL consumerurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+        );
+        Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+        DemoService demoProxy = (DemoService)proxy.getProxy(reference);
+        Type type = demoProxy.enumlength(Type.High);
+        System.out.println(type);
+        Assert.assertEquals(Type.High, type);
+        reference.destroy();
+    }
+//    @Test
+    //测试2.0.5调用2.0.3的兼容
+    public void testGenricEnumCompat(){
+        int port = 20880;
+        URL consumerurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+        );
+        Invoker<GenericService> reference = protocol.refer(GenericService.class, consumerurl);
+        
+        GenericService demoProxy = (GenericService)proxy.getProxy(reference);
+        Object obj = demoProxy.$invoke("enumlength", new String[]{Type[].class.getName()}, new Object[]{new Type[]{Type.High,Type.High}});
+        System.out.println("obj---------->"+obj);
+        reference.destroy();
+    }
+    
+//    @Test
+    //测试2.0.5调用2.0.3的兼容 自定义类型参数中包含enum类型
+    public void testGenricCustomArg(){
+        
+        int port = 20880;
+        URL consumerurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout=2000000"
+        );
+        Invoker<GenericService> reference = protocol.refer(GenericService.class, consumerurl);
+        
+        GenericService demoProxy = (GenericService)proxy.getProxy(reference);
+        Map<String, Object> arg = new HashMap<String, Object>();
+        arg.put("type", "High");
+        arg.put("name", "hi");
+        
+        Object obj = demoProxy.$invoke("get", new String[]{"com.alibaba.dubbo.rpc.CustomArgument"}, new Object[]{arg});
+        System.out.println("obj---------->"+obj);
+        reference.destroy();
+    }
+    
+//  @Ignore
+//  @Test
+  public void testGenericExport() throws InterruptedException{
+      int port = NetUtils.getAvailablePort();
+      port = 20880;
+      URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+              );
+      DemoService demo = new DemoServiceImpl();
+      Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+      protocol.export(invoker);
+      
+      
+      //SERVER
+      Thread.sleep(Integer.MAX_VALUE);
+  }
+    
+    @Test
+    public void testGenericEnum() throws InterruptedException{
+        int port = NetUtils.getAvailablePort();
+        URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+                );
+        DemoService demo = new DemoServiceImpl();
+        Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+        protocol.export(invoker);
+        
+        URL consumerurl = serviceurl;
+        
+        Invoker<GenericService> reference = protocol.refer(GenericService.class, consumerurl);
+        
+        GenericService demoProxy = (GenericService)proxy.getProxy(reference);
+        Object obj = demoProxy.$invoke("enumlength", new String[]{Type[].class.getName()}, new Object[]{new Type[]{Type.High,Type.High}});
+        System.out.println("obj---------->"+obj);
+        
+        invoker.destroy();
+        reference.destroy();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/NonSerialized.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/NonSerialized.java
new file mode 100644
index 0000000..6ec783a
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/NonSerialized.java
@@ -0,0 +1,25 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;

+

+/**

+ * NonSerialized

+ * 

+ * @author william.liangf

+ */

+public class NonSerialized {

+

+}

diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Person.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Person.java
new file mode 100644
index 0000000..392330a
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Person.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;

+

+import java.io.Serializable;

+

+/**

+ * Person.java

+ * 

+ * @author tony.chenl

+ */

+public class Person implements Serializable {

+

+    private static final long serialVersionUID = 1L;

+    private String            name;

+    private int               age;

+

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        this.name = name;

+    }

+

+    public int getAge() {

+        return age;

+    }

+

+    public void setAge(int age) {

+        this.age = age;

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.java
new file mode 100644
index 0000000..65f60f9
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.java
@@ -0,0 +1,66 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;

+

+import java.util.Collection;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * TODO Comment of ProtocolUtils

+ * 

+ * @author william.liangf

+ */

+public class ProtocolUtils {

+

+    private static Protocol     protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+    public static ProxyFactory proxy    = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+

+    public static <T> T refer(Class<T> type, String url) {

+        return refer(type, URL.valueOf(url));

+    }

+

+    public static <T> T refer(Class<T> type, URL url) {

+        return proxy.getProxy(protocol.refer(type, url));

+    }

+    

+    public static Invoker<?> referInvoker(Class<?> type, URL url) {

+        return (Invoker<?>)protocol.refer(type, url);

+    }

+

+    public static <T> Exporter<T> export(T instance, Class<T> type, String url) {

+        return export(instance, type, URL.valueOf(url));

+    }

+

+    public static <T> Exporter<T> export(T instance, Class<T> type, URL url) {

+        return protocol.export(proxy.getInvoker(instance, type, url));

+    }

+

+    public static void closeAll() {

+        DubboProtocol.getDubboProtocol().destroy();

+        Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();

+        for (ExchangeServer server : servers) {

+            server.close();

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteService.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteService.java
new file mode 100644
index 0000000..1bf8400
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteService.java
@@ -0,0 +1,26 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteService extends Remote
+{
+	String sayHello(String name) throws RemoteException;
+
+	String getThreadName() throws RemoteException;
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteServiceImpl.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteServiceImpl.java
new file mode 100644
index 0000000..53aa847
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteServiceImpl.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;
+
+import java.rmi.RemoteException;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+public class RemoteServiceImpl implements RemoteService
+{
+	public String getThreadName() throws RemoteException
+	{
+		System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+		return Thread.currentThread().getName();
+	}
+
+	public String sayHello(String name) throws RemoteException
+	{
+		return "hello " + name + "@" + RemoteServiceImpl.class.getName();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Type.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Type.java
new file mode 100644
index 0000000..ebbadb0
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Type.java
@@ -0,0 +1,21 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.support;
+
+public enum Type
+{
+	High, Normal, Lower
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandlerTest.java
new file mode 100644
index 0000000..1e0a0ab
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandlerTest.java
@@ -0,0 +1,121 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import static org.junit.Assert.assertEquals;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.AfterClass;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;

+

+/**

+ * ChangeTelnetHandlerTest.java

+ * 

+ * @author tony.chenl

+ */

+public class ChangeTelnetHandlerTest {

+

+    private static TelnetHandler change = new ChangeTelnetHandler();

+    private Channel              mockChannel;

+    private Invoker<DemoService> mockInvoker;

+

+    @SuppressWarnings("unchecked")

+    @Before

+    public void setUp() {

+        mockChannel = EasyMock.createMock(Channel.class);

+        mockInvoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService").anyTimes();

+        mockChannel.setAttribute("telnet.service", "DemoService");

+        EasyMock.expectLastCall().anyTimes();

+        mockChannel.setAttribute("telnet.service", "com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService");

+        EasyMock.expectLastCall().anyTimes();

+        mockChannel.setAttribute("telnet.service", "demo");

+        EasyMock.expectLastCall().anyTimes();

+        mockChannel.removeAttribute("telnet.service");

+        EasyMock.expectLastCall().anyTimes();

+        EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20883/demo")).anyTimes();

+        EasyMock.replay(mockChannel, mockInvoker);

+    }

+

+    @AfterClass

+    public static void tearDown() {

+

+    }

+

+    @After

+    public void after() {

+        ProtocolUtils.closeAll();

+        EasyMock.reset(mockChannel, mockInvoker);

+    }

+

+    @Test

+    public void testChangeSimpleName() throws RemotingException {

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = change.telnet(mockChannel, "DemoService");

+        assertEquals("Used the DemoService as default.\r\nYou can cancel default service by command: cd /", result);

+    }

+

+    @Test

+    public void testChangeName() throws RemotingException {

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = change.telnet(mockChannel, "com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService");

+        assertEquals("Used the com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService as default.\r\nYou can cancel default service by command: cd /",

+                     result);

+    }

+

+    @Test

+    public void testChangePath() throws RemotingException {

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = change.telnet(mockChannel, "demo");

+        assertEquals("Used the demo as default.\r\nYou can cancel default service by command: cd /", result);

+    }

+

+    @Test

+    public void testChangeMessageNull() throws RemotingException {

+        String result = change.telnet(mockChannel, null);

+        assertEquals("Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService", result);

+    }

+

+    @Test

+    public void testChangeServiceNotExport() throws RemotingException {

+        String result = change.telnet(mockChannel, "demo");

+        assertEquals("No such service demo", result);

+    }

+

+    @Test

+    public void testChangeCancel() throws RemotingException {

+        String result = change.telnet(mockChannel, "..");

+        assertEquals("Cancelled default service com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService.", result);

+    }

+

+    @Test

+    public void testChangeCancel2() throws RemotingException {

+        String result = change.telnet(mockChannel, "/");

+        assertEquals("Cancelled default service com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService.", result);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandlerTest.java
new file mode 100644
index 0000000..531db00
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandlerTest.java
@@ -0,0 +1,69 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import static org.junit.Assert.assertEquals;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.Test;

+

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+

+/**

+ * CountTelnetHandlerTest.java

+ * 

+ * @author tony.chenl

+ */

+public class CurrentTelnetHandlerTest {

+

+    private static TelnetHandler count = new CurrentTelnetHandler();

+    private Channel              mockChannel;

+

+    @After

+    public void after() {

+        EasyMock.reset(mockChannel);

+    }

+

+    @Test

+    public void testService() throws RemotingException {

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService").anyTimes();

+        EasyMock.replay(mockChannel);

+        String result = count.telnet(mockChannel, "");

+        assertEquals("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService", result);

+    }

+

+    @Test

+    public void testSlash() throws RemotingException {

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();

+        EasyMock.replay(mockChannel);

+        String result = count.telnet(mockChannel, "");

+        assertEquals("/", result);

+    }

+    

+    @Test

+    public void testMessageError() throws RemotingException {

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();

+        EasyMock.replay(mockChannel);

+        String result = count.telnet(mockChannel, "test");

+        assertEquals("Unsupported parameter test for pwd.", result);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java
new file mode 100644
index 0000000..e26d1ae
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java
@@ -0,0 +1,109 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertTrue;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;

+

+/**

+ * CountTelnetHandlerTest.java

+ * 

+ * @author tony.chenl

+ */

+public class InvokerTelnetHandlerTest {

+

+    private static TelnetHandler invoke = new InvokeTelnetHandler();

+    private Channel              mockChannel;

+    private Invoker<DemoService> mockInvoker;

+

+    @After

+    public void after() {

+       ProtocolUtils.closeAll();

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testInvokeDefaultSService() throws RemotingException {

+        mockInvoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20883/demo")).anyTimes();

+        EasyMock.expect(mockInvoker.invoke((Invocation) EasyMock.anyObject())).andReturn(new RpcResult("ok")).anyTimes();

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService").anyTimes();

+        EasyMock.expect(mockChannel.getLocalAddress()).andReturn(NetUtils.toAddress("127.0.0.1:5555")).anyTimes();

+        EasyMock.expect(mockChannel.getRemoteAddress()).andReturn(NetUtils.toAddress("127.0.0.1:20883")).anyTimes();

+        EasyMock.replay(mockChannel, mockInvoker);

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = invoke.telnet(mockChannel, "DemoService.echo(\"ok\")");

+        assertTrue(result.contains("Use default service com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService.\r\n\"ok\"\r\n"));

+        EasyMock.reset(mockChannel, mockInvoker);

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testInvokeAutoFindMethod() throws RemotingException {

+        mockInvoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20883/demo")).anyTimes();

+        EasyMock.expect(mockInvoker.invoke((Invocation) EasyMock.anyObject())).andReturn(new RpcResult("ok")).anyTimes();

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();

+        EasyMock.expect(mockChannel.getLocalAddress()).andReturn(NetUtils.toAddress("127.0.0.1:5555")).anyTimes();

+        EasyMock.expect(mockChannel.getRemoteAddress()).andReturn(NetUtils.toAddress("127.0.0.1:20883")).anyTimes();

+        EasyMock.replay(mockChannel, mockInvoker);

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = invoke.telnet(mockChannel, "echo(\"ok\")");

+        assertTrue(result.contains("ok"));

+        EasyMock.reset(mockChannel, mockInvoker);

+    }

+

+    @Test

+    public void testMessageNull() throws RemotingException {

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();

+        EasyMock.replay(mockChannel);

+        String result = invoke.telnet(mockChannel, null);

+        assertEquals("Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})",

+                     result);

+        EasyMock.reset(mockChannel);

+    }

+

+    @Test

+    public void testInvaildMessage() throws RemotingException {

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();

+        EasyMock.replay(mockChannel);

+        String result = invoke.telnet(mockChannel, "(");

+        assertEquals("Invalid parameters, format: service.method(args)", result);

+        EasyMock.reset(mockChannel);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java
new file mode 100644
index 0000000..54986eb
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java
@@ -0,0 +1,171 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import static org.junit.Assert.assertEquals;

+

+import java.lang.reflect.Method;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;

+

+/**

+ * CountTelnetHandlerTest.java

+ * 

+ * @author tony.chenl

+ */

+public class ListTelnetHandlerTest {

+

+    private static TelnetHandler list = new ListTelnetHandler();

+    private Channel              mockChannel;

+    private Invoker<DemoService> mockInvoker;

+    private static String        detailMethods;

+    private static String        methodsName;

+

+    @BeforeClass

+    public static void setUp() {

+        StringBuilder buf = new StringBuilder();

+        StringBuilder buf2 = new StringBuilder();

+        Method[] methods = DemoService.class.getMethods();

+        for (Method method : methods) {

+            if (buf.length() > 0) {

+                buf.append("\r\n");

+            }

+            if (buf2.length() > 0) {

+                buf2.append("\r\n");

+            }

+            buf2.append(method.getName());

+            buf.append(ReflectUtils.getName(method));

+        }

+        detailMethods = buf.toString();

+        methodsName = buf2.toString();

+        

+        ProtocolUtils.closeAll();

+    }

+    

+    @After

+    public void after() {

+        ProtocolUtils.closeAll();

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testListDetailService() throws RemotingException {

+        mockInvoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20885/demo")).anyTimes();

+        EasyMock.expect(mockInvoker.invoke((Invocation) EasyMock.anyObject())).andReturn(new RpcResult("ok")).anyTimes();

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService").anyTimes();

+        EasyMock.replay(mockChannel, mockInvoker);

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = list.telnet(mockChannel, "-l DemoService");

+        assertEquals(detailMethods, result);

+        EasyMock.reset(mockChannel, mockInvoker);

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testListService() throws RemotingException {

+        mockInvoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20885/demo")).anyTimes();

+        EasyMock.expect(mockInvoker.invoke((Invocation) EasyMock.anyObject())).andReturn(new RpcResult("ok")).anyTimes();

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService").anyTimes();

+        EasyMock.replay(mockChannel, mockInvoker);

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = list.telnet(mockChannel, "DemoService");

+        assertEquals(methodsName, result);

+        EasyMock.reset(mockChannel, mockInvoker);

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testList() throws RemotingException {

+        mockInvoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20885/demo")).anyTimes();

+        EasyMock.expect(mockInvoker.invoke((Invocation) EasyMock.anyObject())).andReturn(new RpcResult("ok")).anyTimes();

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();

+        EasyMock.replay(mockChannel, mockInvoker);

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = list.telnet(mockChannel, "");

+        assertEquals("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService", result);

+        EasyMock.reset(mockChannel);

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testListDetail() throws RemotingException {

+        int port = NetUtils.getAvailablePort();

+        mockInvoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:"+port+"/demo")).anyTimes();

+        EasyMock.expect(mockInvoker.invoke((Invocation) EasyMock.anyObject())).andReturn(new RpcResult("ok")).anyTimes();

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();

+        EasyMock.replay(mockChannel, mockInvoker);

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = list.telnet(mockChannel, "-l");

+        assertEquals("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService -> dubbo://" + NetUtils.getLocalHost()

+                     + ":"+port+"/demo?localhost=true", result);

+        EasyMock.reset(mockChannel);

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testListDefault() throws RemotingException {

+        mockInvoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20885/demo")).anyTimes();

+        EasyMock.expect(mockInvoker.invoke((Invocation) EasyMock.anyObject())).andReturn(new RpcResult("ok")).anyTimes();

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService").anyTimes();

+        EasyMock.replay(mockChannel, mockInvoker);

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+        String result = list.telnet(mockChannel, "");

+        assertEquals("Use default service com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService.\r\n\r\n"

+                     + methodsName, result);

+        EasyMock.reset(mockChannel);

+    }

+

+    @Test

+    public void testInvaildMessage() throws RemotingException {

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();

+        EasyMock.replay(mockChannel);

+        String result = list.telnet(mockChannel, "xx");

+        assertEquals("No such service xx", result);

+        EasyMock.reset(mockChannel);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandlerTest.java
new file mode 100644
index 0000000..5822c72
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandlerTest.java
@@ -0,0 +1,57 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import static org.junit.Assert.assertTrue;

+

+import org.easymock.EasyMock;

+import org.junit.Test;

+

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+

+/**

+ * LogTelnetHandlerTest.java

+ * 

+ * @author tony.chenl

+ */

+public class LogTelnetHandlerTest {

+

+    private static TelnetHandler log = new LogTelnetHandler();

+    private Channel              mockChannel;

+

+    @Test

+    public void testChangeLogLevel() throws RemotingException {

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.replay(mockChannel);

+        String result = log.telnet(mockChannel, "error");

+        assertTrue(result.contains("\r\nCURRENT LOG LEVEL:ERROR"));

+        String result2 = log.telnet(mockChannel, "warn");

+        assertTrue(result2.contains("\r\nCURRENT LOG LEVEL:WARN"));

+        EasyMock.reset(mockChannel);

+    }

+

+    @Test

+    public void testPrintLog() throws RemotingException {

+        mockChannel = EasyMock.createMock(Channel.class);

+        EasyMock.replay(mockChannel);

+        String result = log.telnet(mockChannel, "100");

+        assertTrue(result.contains("CURRENT LOG APPENDER"));

+        EasyMock.reset(mockChannel);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandlerTest.java
new file mode 100644
index 0000000..2b2bf4f
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandlerTest.java
@@ -0,0 +1,97 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.dubbo.telnet;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertTrue;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.remoting.RemotingException;

+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;

+import com.alibaba.dubbo.remoting.exchange.Exchangers;

+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;

+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;

+

+/**

+ * PortTelnetHandlerTest.java

+ * 

+ * @author tony.chenl

+ */

+public class PortTelnetHandlerTest {

+

+    private static TelnetHandler port = new PortTelnetHandler();

+    private Invoker<DemoService> mockInvoker;

+

+    @SuppressWarnings("unchecked")

+    @Before

+    public void before() {

+        mockInvoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20887/demo")).anyTimes();

+        EasyMock.replay(mockInvoker);

+        DubboProtocol.getDubboProtocol().export(mockInvoker);

+    }

+

+    @After

+    public void after() {

+        EasyMock.reset(mockInvoker);

+        ProtocolUtils.closeAll();

+    }

+

+    @Test

+    public void testListClient() throws RemotingException {

+        ExchangeClient client1 = Exchangers.connect("dubbo://127.0.0.1:20887/demo");

+        ExchangeClient client2 = Exchangers.connect("dubbo://127.0.0.1:20887/demo");

+        String result = port.telnet(null, "-l 20887");

+        assertTrue(result.contains(client1.getLocalAddress().toString()));

+        assertTrue(result.contains(client2.getLocalAddress().toString()));

+

+    }

+

+    @Test

+    public void testListDetail() throws RemotingException {

+        String result = port.telnet(null, "-l");

+        assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20887", result);

+    }

+

+    @Test

+    public void testListAllPort() throws RemotingException {

+        String result = port.telnet(null, "");

+        assertEquals("20887", result);

+    }

+

+    @Test

+    public void testErrorMessage() throws RemotingException {

+        String result = port.telnet(null, "a");

+        assertEquals("Illegal port a, must be integer.", result);

+    }

+

+    @Test

+    public void testNoPort() throws RemotingException {

+        String result = port.telnet(null, "-l 20880");

+        assertEquals("No such port 20880", result);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/resources/log4j.xml b/dubbo-rpc-default/src/test/resources/log4j.xml
new file mode 100644
index 0000000..788ed66
--- /dev/null
+++ b/dubbo-rpc-default/src/test/resources/log4j.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+	<!-- ===================================================================== -->
+	<!-- 以下是appender的定义 -->
+	<!-- ===================================================================== -->
+	<appender name="dubbo" class="com.alibaba.dubbo.common.utils.DubboAppender">
+		<param name="encoding" value="GBK" />
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="%d %p [%c:%M] - %m%n" />
+		</layout>
+		<!-- <filter class="org.apache.log4j.varia.LevelRangeFilter">
+			<param name="LevelMin" value="DEBUG" />
+			<param name="LevelMax" value="DEBUG" />
+		</filter> -->
+	</appender>

+	<appender name="FILE" class="org.apache.log4j.FileAppender">

+		<param name="File" value="dubbo.log" />

+		<layout class="org.apache.log4j.PatternLayout">

+			<!-- <param name="ConversionPattern" value="[%t %d{dd/MM/yy hh:mm:ss:sss 

+				z}] %5p %c{2}: %L %m%n" /> -->

+			<param name="ConversionPattern" value="[%t %l %d{dd/MM/yy hh:mm:ss:sss z}] %5p %m %n" />

+		</layout>

+	</appender>
+	<root>
+		<level value="INFO" />
+		<appender-ref ref="dubbo" />

+		<appender-ref ref="FILE" />
+	</root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/pom.xml b/dubbo-rpc-hessian/pom.xml
new file mode 100644
index 0000000..ea92905
--- /dev/null
+++ b/dubbo-rpc-hessian/pom.xml
@@ -0,0 +1,53 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-rpc-hessian</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo Hessian RPC Module</name>
+	<description>The hessian rpc module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-rpc</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-remoting-http</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+		    <groupId>com.caucho</groupId>
+		    <artifactId>hessian</artifactId>
+		</dependency>

+		<dependency>

+		    <groupId>org.apache.httpcomponents</groupId>

+		    <artifactId>httpclient</artifactId>

+		    <scope>provided</scope>

+		    <optional>true</optional>

+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.java
new file mode 100644
index 0000000..87beb4f
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.java
@@ -0,0 +1,117 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.hessian;
+
+import java.io.IOException;

+import java.util.ArrayList;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.remoting.http.HttpBinder;

+import com.alibaba.dubbo.remoting.http.HttpHandler;

+import com.alibaba.dubbo.remoting.http.HttpServer;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;

+
+/**
+ * http rpc support.
+ * 
+ * @author qianlei
+ */
+public class HessianProtocol extends AbstractProtocol {
+
+    private final Map<String, HttpServer> serverMap = new ConcurrentHashMap<String, HttpServer>();
+
+    private HttpBinder               httpTransporter;
+

+    private ProxyFactory             proxyFactory;

+

+    public void setHttpTransporter(HttpBinder httpTransporter) {

+        this.httpTransporter = httpTransporter;

+    }

+
+    public void setProxyFactory(ProxyFactory proxyFactory) {
+        this.proxyFactory = proxyFactory;
+    }
+
+    public int getDefaultPort() {
+        return 80;
+    }

+    

+    private class HessianHandler implements HttpHandler {

+        

+        public void handle(HttpServletRequest request, HttpServletResponse response)

+                throws IOException, ServletException {

+            String uri = request.getRequestURI();

+            HessianRpcExporter<?> exporter = (HessianRpcExporter<?>) exporterMap.get(uri);

+            exporter.handle(request, response);

+        }

+        

+    }
+
+    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+        final URL url = invoker.getUrl();
+        final String uri = url.getAbsolutePath(); // service uri also exporter cache key.
+
+        String addr = url.getIp() + ":" + url.getPort();
+        HttpServer server = serverMap.get(addr);
+        if (server == null) {
+            server = httpTransporter.bind(url, new HessianHandler());
+            serverMap.put(addr, server);
+        }
+
+        HessianRpcExporter<T> exporter = new HessianRpcExporter<T>(invoker, proxyFactory) {
+            public void unexport() {
+                super.unexport();
+                exporterMap.remove(uri);

+            }
+        };
+        exporterMap.put(uri, exporter);
+        return exporter;
+    }
+
+    public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
+        Invoker<T> invoker = new HessianRpcInvoker<T>(serviceType, url, proxyFactory);
+        invokers.add(invoker);
+        return invoker;
+    }
+
+    public void destroy() {
+        super.destroy();
+        for (String key : new ArrayList<String>(serverMap.keySet())) {
+            HttpServer server = serverMap.remove(key);
+            if (server != null) {
+                try {
+                    if (logger.isInfoEnabled()) {
+                        logger.info("Close hessian server " + server.getUrl());
+                    }
+                    server.close();
+                } catch (Throwable t) {
+                    logger.warn(t.getMessage(), t);
+                }
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcExporter.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcExporter.java
new file mode 100644
index 0000000..6994d2e
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcExporter.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.hessian;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.dubbo.remoting.http.HttpHandler;

+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.protocol.AbstractExporter;
+import com.caucho.hessian.server.HessianSkeleton;
+
+/**
+ * hessian rpc exporter.
+ * 
+ * @author qian.lei
+ */
+public class HessianRpcExporter<T> extends AbstractExporter<T> implements HttpHandler {
+
+    private HessianSkeleton skeleton;
+
+    public HessianRpcExporter(Invoker<T> invoker, ProxyFactory proxyFactory) {
+        super(invoker);
+        skeleton = new HessianSkeleton(proxyFactory.getProxy(invoker), invoker.getInterface());
+    }
+
+    public void handle(HttpServletRequest request, HttpServletResponse response)
+            throws IOException, ServletException {
+        if (! request.getMethod().equalsIgnoreCase("POST")) {
+            response.setStatus(500);
+        } else {
+            RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(),
+                    request.getRemotePort());
+            try {
+                skeleton.invoke(request.getInputStream(), response.getOutputStream());
+            } catch (Throwable e) {
+                throw new ServletException(e);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcInvoker.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcInvoker.java
new file mode 100644
index 0000000..fc70d42
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcInvoker.java
@@ -0,0 +1,105 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.hessian;
+
+import java.net.SocketTimeoutException;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;

+import com.caucho.hessian.HessianException;

+import com.caucho.hessian.client.HessianConnectionException;

+import com.caucho.hessian.client.HessianConnectionFactory;

+import com.caucho.hessian.client.HessianProxyFactory;

+import com.caucho.hessian.io.HessianMethodSerializationException;

+
+/**
+ * hessian rpc invoker.
+ * 
+ * @author qianlei
+ */
+public class HessianRpcInvoker<T> extends AbstractInvoker<T> {
+
+    protected static final String HESSIAN_EXCEPTION_PREFIX = HessianException.class.getPackage().getName() + "."; //fix by tony.chenl
+
+    protected Invoker<T>   invoker;

+    

+    protected HessianConnectionFactory hessianConnectionFactory = new HttpClientConnectionFactory();
+
+    @SuppressWarnings("unchecked")
+    public HessianRpcInvoker(Class<T> serviceType, URL url, ProxyFactory proxyFactory){
+        super(serviceType, url);
+        HessianProxyFactory hessianProxyFactory = new HessianProxyFactory();

+        String client = url.getParameter(Constants.CLIENT_KEY, Constants.DEFAULT_HTTP_CLIENT);
+        if ("httpclient".equals(client)) {

+            hessianProxyFactory.setConnectionFactory(hessianConnectionFactory);

+        } else if (client != null && client.length() > 0 && ! Constants.DEFAULT_HTTP_CLIENT.equals(client)) {

+            throw new IllegalStateException("Unsupported http protocol client=\"" + client + "\"!");

+        }

+        int timeout = url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);

+        hessianProxyFactory.setConnectTimeout(timeout);
+        hessianProxyFactory.setReadTimeout(timeout);
+        invoker = proxyFactory.getInvoker((T)hessianProxyFactory.create(serviceType, url.setProtocol("http").toJavaURL(), Thread.currentThread().getContextClassLoader()), serviceType, url);
+    }
+
+    @Override
+    protected Result doInvoke(Invocation invocation) throws Throwable {
+        try {

+            Result result = invoker.invoke(invocation);

+            Throwable e = result.getException();

+            if (e != null) {

+                String name = e.getClass().getName();

+                if (name.startsWith(HESSIAN_EXCEPTION_PREFIX)) {

+                    RpcException re = new RpcException("Failed to invoke remote service: " + getInterface() + ", method: "

+                            + invocation.getMethodName() + ", cause: " + e.getMessage(), e);

+                    throw setRpcExceptionCode(e, re);

+                }

+            }
+            return result;
+        } catch (RpcException e) {
+            throw setRpcExceptionCode(e.getCause(), e);
+        } catch (HessianException e) {

+            throw setRpcExceptionCode(e, new RpcException("Failed to invoke remote service: " + getInterface() + ", method: "

+                    + invocation.getMethodName() + ", cause: " + e.getMessage(), e));

+        } catch (Throwable e) {
+            return new RpcResult(e);
+        }
+    }

+    

+    private RpcException setRpcExceptionCode(Throwable e, RpcException re) {

+        if (e != null) {

+            if (e instanceof HessianConnectionException) {

+                re.setCode(RpcException.NETWORK_EXCEPTION);

+                if (e.getCause() != null) {

+                    Class<?> cls = e.getCause().getClass();

+                    if (SocketTimeoutException.class.equals(cls)) {

+                        re.setCode(RpcException.TIMEOUT_EXCEPTION);

+                    }

+                }

+            } else if (e instanceof HessianMethodSerializationException) {

+                re.setCode(RpcException.SERIALIZATION_EXCEPTION);

+            }

+        }

+        return re;

+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnection.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnection.java
new file mode 100644
index 0000000..e8cf9f1
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnection.java
@@ -0,0 +1,88 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.hessian;

+

+import java.io.ByteArrayOutputStream;

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+import java.net.URL;

+

+import org.apache.http.HttpResponse;

+import org.apache.http.client.HttpClient;

+import org.apache.http.client.methods.HttpPost;

+import org.apache.http.entity.ByteArrayEntity;

+import org.apache.http.message.BasicHeader;

+

+import com.caucho.hessian.client.HessianConnection;

+

+/**

+ * HttpClientConnection

+ * 

+ * @author william.liangf

+ */

+public class HttpClientConnection implements HessianConnection {

+    

+    private final HttpClient httpClient;

+

+    private final ByteArrayOutputStream output;

+    

+    private final HttpPost request;

+    

+    private volatile HttpResponse response;

+

+    public HttpClientConnection(HttpClient httpClient, URL url) {

+        this.httpClient = httpClient;

+        this.output = new ByteArrayOutputStream();

+        this.request = new HttpPost(url.toString());

+    }

+

+    public void addHeader(String key, String value) {

+        request.addHeader(new BasicHeader(key, value));

+    }

+

+    public OutputStream getOutputStream() throws IOException {

+        return output;

+    }

+

+    public void sendRequest() throws IOException {

+        request.setEntity(new ByteArrayEntity(output.toByteArray()));

+        this.response = httpClient.execute(request);

+    }

+

+    public int getStatusCode() {

+        return response == null || response.getStatusLine() == null ? 0 : response.getStatusLine().getStatusCode();

+    }

+

+    public String getStatusMessage() {

+        return response == null || response.getStatusLine() == null ? null :  response.getStatusLine().getReasonPhrase();

+    }

+

+    public InputStream getInputStream() throws IOException {

+        return response == null || response.getEntity() == null ? null : response.getEntity().getContent();

+    }

+

+    public void close() throws IOException {

+        HttpPost request = this.request;

+        if (request != null) {

+            request.abort();

+        }

+    }

+

+    public void destroy() throws IOException {

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java
new file mode 100644
index 0000000..8829b0c
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.hessian;

+

+import java.io.IOException;

+import java.net.URL;

+

+import org.apache.http.client.HttpClient;

+import org.apache.http.impl.client.DefaultHttpClient;

+import org.apache.http.params.HttpConnectionParams;

+

+import com.caucho.hessian.client.HessianConnection;

+import com.caucho.hessian.client.HessianConnectionFactory;

+import com.caucho.hessian.client.HessianProxyFactory;

+

+/**

+ * HttpClientConnectionFactory

+ * 

+ * @author william.liangf

+ */

+public class HttpClientConnectionFactory implements HessianConnectionFactory {

+    

+    private final HttpClient httpClient = new DefaultHttpClient();

+    

+    public void setHessianProxyFactory(HessianProxyFactory factory) {

+        HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), (int) factory.getConnectTimeout());

+        HttpConnectionParams.setSoTimeout(httpClient.getParams(), (int) factory.getReadTimeout());

+    }

+

+    public HessianConnection open(URL url) throws IOException {

+        return new HttpClientConnection(httpClient, url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-hessian/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..2738fa9
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocolTest.java b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocolTest.java
new file mode 100644
index 0000000..5939f03
--- /dev/null
+++ b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocolTest.java
@@ -0,0 +1,112 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.hessian;

+

+import static org.junit.Assert.fail;

+import junit.framework.Assert;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.protocol.hessian.HessianServiceImpl.MyException;

+

+/**

+ * HessianProtocolTest

+ * 

+ * @author william.liangf

+ */

+public class HessianProtocolTest {

+    

+    @Test

+    public void testHessianProtocol() {

+        HessianServiceImpl server = new HessianServiceImpl();

+        Assert.assertFalse(server.isCalled());

+        ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+        URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0");

+        Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));

+        Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);

+        HessianService client = proxyFactory.getProxy(invoker);

+        String result = client.sayHello("haha");

+        Assert.assertTrue(server.isCalled());

+        Assert.assertEquals("Hello, haha", result);

+        invoker.destroy();

+        exporter.unexport();

+    }

+

+    @Test

+    public void testHttpClient() {

+        HessianServiceImpl server = new HessianServiceImpl();

+        Assert.assertFalse(server.isCalled());

+        ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+        URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0&client=httpclient");

+        Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));

+        Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);

+        HessianService client = proxyFactory.getProxy(invoker);

+        String result = client.sayHello("haha");

+        Assert.assertTrue(server.isCalled());

+        Assert.assertEquals("Hello, haha", result);

+        invoker.destroy();

+        exporter.unexport();

+    }

+    

+    @Test

+    public void testTimeOut() {

+        HessianServiceImpl server = new HessianServiceImpl();

+        ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+        URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0&timeout=10");

+        Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));

+        Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);

+        HessianService client = proxyFactory.getProxy(invoker);

+        try {

+            client.timeOut(6000);

+            fail();

+        } catch (RpcException expected) {

+            Assert.assertEquals(true, expected.isTimeout());

+        }finally{

+            invoker.destroy();

+            exporter.unexport();

+        }

+        

+    }

+    

+    @Test

+    public void testCustomException() {

+        HessianServiceImpl server = new HessianServiceImpl();

+        ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+        URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0");

+        Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));

+        Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);

+        HessianService client = proxyFactory.getProxy(invoker);

+        try {

+            client.customException();

+            fail();

+        } catch (MyException expected) {

+        }

+        invoker.destroy();

+        exporter.unexport();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianService.java b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianService.java
new file mode 100644
index 0000000..133be00
--- /dev/null
+++ b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianService.java
@@ -0,0 +1,32 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.hessian;

+

+

+/**

+ * HessianService

+ * 

+ * @author william.liangf

+ */

+public interface HessianService {

+    

+    String sayHello(String name);

+    

+     void timeOut(int millis);

+    

+     String customException();

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianServiceImpl.java b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianServiceImpl.java
new file mode 100644
index 0000000..c9d7095
--- /dev/null
+++ b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianServiceImpl.java
@@ -0,0 +1,56 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.hessian;

+

+/**

+ * HessianServiceImpl

+ * 

+ * @author william.liangf

+ */

+public class HessianServiceImpl implements HessianService {

+    

+    private boolean called;

+

+    public String sayHello(String name) {

+        called = true;

+        return "Hello, " + name;

+    }

+

+    public boolean isCalled() {

+        return called;

+    }

+    

+    public void timeOut(int millis) {

+        try {

+            Thread.sleep(millis);

+        } catch (InterruptedException e) {

+            e.printStackTrace();

+        }

+    }

+    

+    public String customException() {

+        throw new MyException("custom exception");

+    }

+

+   static class MyException extends RuntimeException{

+        

+        private static final long serialVersionUID = -3051041116483629056L;

+

+        public MyException(String message) {

+            super(message);

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/pom.xml b/dubbo-rpc-injvm/pom.xml
new file mode 100644
index 0000000..4b67d1b
--- /dev/null
+++ b/dubbo-rpc-injvm/pom.xml
@@ -0,0 +1,38 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-rpc-injvm</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo InJVM RPC Module</name>
+	<description>The injvm rpc module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-rpc</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmExporter.java
new file mode 100644
index 0000000..bc234e7
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmExporter.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.injvm;

+

+import java.util.Map;

+

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.protocol.AbstractExporter;

+

+/**

+ * InjvmExporter

+ * 

+ * @author william.liangf

+ */

+class InjvmExporter<T> extends AbstractExporter<T> {

+

+    private final String key;

+    

+    private final Map<String, Exporter<?>> exporterMap;

+

+    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap){

+        super(invoker);

+        this.key = key;

+        this.exporterMap = exporterMap;

+        exporterMap.put(key, this);

+    }

+

+    public void unexport() {

+        super.unexport();

+        exporterMap.remove(key);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmInvoker.java b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmInvoker.java
new file mode 100644
index 0000000..eeb5f6a
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmInvoker.java
@@ -0,0 +1,54 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.injvm;

+

+import java.util.Map;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;

+

+/**

+ * InjvmInvoker

+ * 

+ * @author william.liangf

+ */

+class InjvmInvoker<T> extends AbstractInvoker<T> {

+

+    private final String key;

+

+    private final Map<String, Exporter<?>> exporterMap;

+

+    InjvmInvoker(Class<T> type, URL url, String key, Map<String, Exporter<?>> exporterMap){

+        super(type, url);

+        this.key = key;

+        this.exporterMap = exporterMap;

+    }

+

+    public Result doInvoke(Invocation invocation) throws Throwable {

+        InjvmExporter<?> exporter = (InjvmExporter<?>) exporterMap.get(key);

+        if (exporter == null)  {

+            throw new RpcException("Service [" + key + "] not found.");

+        }

+        RpcContext.getContext().setRemoteAddress(NetUtils.LOCALHOST, 0);

+        return exporter.getInvoker().invoke(invocation);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.java b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.java
new file mode 100644
index 0000000..c821639
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.java
@@ -0,0 +1,49 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.injvm;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;

+
+/**
+ * InjvmProtocol
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class InjvmProtocol extends AbstractProtocol implements Protocol {
+    
+    public static final String NAME = "injvm";
+
+    public static final int DEFAULT_PORT = 0;
+
+    public int getDefaultPort() {
+        return DEFAULT_PORT;
+    }
+
+    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {

+        return new InjvmExporter<T>(invoker, serviceKey(invoker.getUrl().setPort(DEFAULT_PORT)), exporterMap);
+    }
+
+    public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
+        return new InjvmInvoker<T>(serviceType, url, serviceKey(url), exporterMap);
+    }
+    
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..09a526c
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoRequest.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoRequest.java
new file mode 100644
index 0000000..961c287
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoRequest.java
@@ -0,0 +1,64 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protococol.injvm;
+
+import java.io.Serializable;
+
+/**
+ * TestRequest.
+ * 
+ * @author qian.lei
+ */
+
+class DemoRequest implements Serializable
+{
+	private static final long serialVersionUID = -2579095288792344869L;
+
+	private String mServiceName;
+	
+	private String mMethodName;
+
+	private Class<?>[] mParameterTypes;
+
+	private Object[] mArguments;
+
+	public DemoRequest(String serviceName,String methodName, Class<?>[] parameterTypes,Object[] args)
+	{
+		mServiceName = serviceName;
+		mMethodName = methodName;
+		mParameterTypes = parameterTypes;
+		mArguments = args;
+	}
+
+	public String getServiceName()
+	{
+		return mServiceName;
+	}
+
+	public String getMethodName()
+	{
+		return mMethodName;
+	}
+
+	public Class<?>[] getParameterTypes() {
+		return mParameterTypes;
+	}
+
+	public Object[] getArguments()
+	{
+		return mArguments;
+	}
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoService.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoService.java
new file mode 100644
index 0000000..9b1a680
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoService.java
@@ -0,0 +1,41 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protococol.injvm;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+	void sayHello(String name);
+
+	String echo(String text);
+
+	long timestamp();
+
+	String getThreadName();
+
+	int getSize(String[] strs);
+
+	int getSize(Object[] os);
+
+	Object invoke(String service, String method) throws Exception;
+
+	int stringLength(String str);
+
+	Type enumlength(Type... types);
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoServiceImpl.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoServiceImpl.java
new file mode 100644
index 0000000..bdb1959
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoServiceImpl.java
@@ -0,0 +1,80 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protococol.injvm;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+/**
+ * DemoServiceImpl
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+	public DemoServiceImpl()
+	{
+		super();
+	}
+
+	public void sayHello(String name) {
+		System.out.println("hello "+name);
+	}
+
+	public String echo(String text)
+	{
+		return text;
+	}
+
+	public long timestamp() {
+		return System.currentTimeMillis();
+	}
+
+	public String getThreadName()
+	{
+		return Thread.currentThread().getName();
+	}
+
+	public int getSize(String[] strs)
+	{
+		if( strs == null )
+			return -1;
+		return strs.length;
+	}
+
+	public int getSize(Object[] os)
+	{
+		if( os == null )
+			return -1;
+		return os.length;
+	}
+
+	public Object invoke(String service, String method) throws Exception
+	{
+		System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+		return service + ":" + method;
+	}
+
+	public Type enumlength(Type... types)
+	{
+		if( types.length == 0 )
+			return Type.Lower;
+		return types[0];
+	}
+
+	public int stringLength(String str)
+	{
+		return str.length();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/IEcho.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/IEcho.java
new file mode 100644
index 0000000..ec98751
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/IEcho.java
@@ -0,0 +1,20 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protococol.injvm;
+
+public interface IEcho {
+    String echo(String e);
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/InjvmProtocolTest.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/InjvmProtocolTest.java
new file mode 100644
index 0000000..727d17b
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/InjvmProtocolTest.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protococol.injvm;
+
+
+import static junit.framework.Assert.assertEquals;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+
+/**
+ * <code>ProxiesTest</code>
+ */
+
+public class InjvmProtocolTest
+{
+    private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+    private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+	@Test
+	public void testLocalProtocol() throws Exception
+	{
+		DemoService service = new DemoServiceImpl();
+		protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService")));
+		service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService")));
+		assertEquals(service.getSize(new String[]{"", "", ""}), 3);
+		service.invoke("injvm://127.0.0.1/TestService", "invoke");
+	}
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/ProtocolTest.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/ProtocolTest.java
new file mode 100644
index 0000000..70787f7
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/ProtocolTest.java
@@ -0,0 +1,68 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protococol.injvm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+/**
+ * @author ding.lid
+ */
+public class ProtocolTest {
+    
+    IEcho echo = new IEcho() {
+        public String echo(String e) {
+            return e;
+        }
+    };
+    
+    ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("javassist");
+    
+    URL url = URL.valueOf("injvm://localhost:0/com.alibaba.dubbo.rpc.support.IEcho");
+    
+    Invoker<IEcho> invoker = proxyFactory.getInvoker(echo, IEcho.class, url);
+    
+    @Test
+    public void test_destroyWontCloseAllProtocol() throws Exception {
+        Protocol autowireProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+        
+        Protocol InjvmProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("injvm");
+        
+        InjvmProtocol.export(invoker);
+        
+        Invoker<IEcho> refer = InjvmProtocol.refer(IEcho.class, url);
+        IEcho echoProxy = proxyFactory.getProxy(refer);
+        
+        assertEquals("ok", echoProxy.echo("ok"));
+        
+        try {
+            autowireProtocol.destroy();
+        } catch (UnsupportedOperationException expected) {
+            assertThat(expected.getMessage(), containsString("of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"));
+        }
+        
+        assertEquals("ok2", echoProxy.echo("ok2"));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/Type.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/Type.java
new file mode 100644
index 0000000..f667ff6
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/Type.java
@@ -0,0 +1,21 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protococol.injvm;
+
+public enum Type
+{
+	High, Normal, Lower
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/pom.xml b/dubbo-rpc-rmi/pom.xml
new file mode 100644
index 0000000..e6e7fd2
--- /dev/null
+++ b/dubbo-rpc-rmi/pom.xml
@@ -0,0 +1,44 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-rpc-rmi</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo RMI RPC Module</name>
+	<description>The rmi rpc module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-rpc</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring</artifactId>
+			<scope>provided</scope>
+			<optional>true</optional>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiExporter.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiExporter.java
new file mode 100644
index 0000000..14ad1f2
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiExporter.java
@@ -0,0 +1,68 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+import java.rmi.Remote;

+import java.rmi.registry.Registry;

+import java.rmi.server.UnicastRemoteObject;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.protocol.AbstractExporter;

+

+/**

+ * Rmi exporter.

+ * 

+ * @author qian.lei

+ */

+public class RmiExporter<T> extends AbstractExporter<T> {

+    

+    private static final Logger Log = LoggerFactory.getLogger(RmiExporter.class);

+

+    private Remote              remote;

+

+    private Registry            registry;

+

+    public RmiExporter(Invoker<T> invoker, Remote remote, Registry registry) {

+        super(invoker);

+        this.remote = remote;

+        this.registry = registry;

+    }

+

+    public void unexport() {

+        super.unexport();

+        // unexport.

+        if (remote != null) {

+            try {

+                UnicastRemoteObject.unexportObject(remote, true);

+            } catch (Exception e) {

+                Log.warn("Unexport rmi object error.", e); //ignore it.

+            }

+            remote = null;

+        }

+        if (registry != null) {

+            try {

+                // unbind.

+                registry.unbind(getInvoker().getUrl().getPath());

+            } catch (Exception e) {

+                Log.warn("Unexport rmi object error.", e); //ignore it.

+            }

+            registry = null;

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiInvoker.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiInvoker.java
new file mode 100644
index 0000000..29a2c3b
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiInvoker.java
@@ -0,0 +1,118 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+import java.io.IOException;

+import java.net.SocketTimeoutException;

+import java.rmi.ConnectException;

+import java.rmi.ConnectIOException;

+import java.rmi.NoSuchObjectException;

+import java.rmi.RemoteException;

+import java.rmi.StubNotFoundException;

+import java.rmi.UnknownHostException;

+import java.rmi.registry.Registry;

+

+import org.omg.CORBA.COMM_FAILURE;

+import org.omg.CORBA.CompletionStatus;

+import org.omg.CORBA.NO_RESPONSE;

+import org.omg.CORBA.SystemException;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;

+

+/**

+ * RmiInvoker.

+ * 

+ * @author qian.lei

+ */

+public class RmiInvoker<T> extends AbstractInvoker<T> {

+    

+    private Registry registry;

+    

+    private RmiProxyFactory rmiProxyFactory;

+

+    private Invoker<T> invoker;

+    

+    private boolean reconnect;

+

+    public RmiInvoker(Registry registry, RmiProxyFactory rmiProxyFactory, Invoker<T> invoker) {

+        super(invoker.getInterface(), invoker.getUrl());

+        this.registry = registry;

+        this.rmiProxyFactory = rmiProxyFactory;

+        this.invoker = invoker;

+        this.reconnect = invoker.getUrl().getParameter(Constants.RECONNECT_KEY, true);

+    }

+

+    @Override

+    protected Result doInvoke(Invocation invocation) throws RpcException {

+        Result result = null;

+        try {

+            result = invoker.invoke(invocation);

+            

+            // 对Rmi的Connection问题进行重试

+            Throwable e = result.getException();

+            if (e != null && isConnectFailure(e) && reconnect) {

+                invoker = rmiProxyFactory.getInvoker(registry.lookup(invoker.getUrl().getPath()), invoker.getInterface(), invoker.getUrl());

+                result = invoker.invoke(invocation);

+            }

+        } catch (RpcException e) {

+            throw setRpcExceptionCode(e.getCause(), e);

+        } catch (Throwable e) {

+            throw setRpcExceptionCode(e, new RpcException(e.getMessage(), e));

+        }

+

+        Throwable e = result.getException();

+        if (e != null && e instanceof RemoteException) {

+            throw setRpcExceptionCode(e, new RpcException("Failed to invoke remote service: " + getInterface() + ", method: "

+                    + invocation.getMethodName() + ", url: " + invoker.getUrl() + ", cause: " + e.getMessage(), e));

+        }

+        return result;

+    }

+    

+    public static RpcException setRpcExceptionCode(Throwable e, RpcException re) {

+        if (e != null && e.getCause() != null) {

+            Class<?> cls = e.getCause().getClass();

+            // 是根据测试Case发现的问题,对RpcException.setCode进行设置

+            if (SocketTimeoutException.class.equals(cls)) {

+                re.setCode(RpcException.TIMEOUT_EXCEPTION);

+            } else if (IOException.class.isAssignableFrom(cls)) {

+                re.setCode(RpcException.NETWORK_EXCEPTION);

+            } else if (ClassNotFoundException.class.isAssignableFrom(cls)) {

+                re.setCode(RpcException.SERIALIZATION_EXCEPTION);

+            }

+        }

+        return re;

+    }

+

+    private static final String ORACLE_CONNECTION_EXCEPTION = "com.evermind.server.rmi.RMIConnectionException";

+

+    private static boolean isConnectFailure(Throwable ex) {

+        return (ex instanceof ConnectException || ex instanceof ConnectIOException ||

+                ex instanceof UnknownHostException || ex instanceof NoSuchObjectException ||

+                ex instanceof StubNotFoundException || isCorbaConnectFailure(ex.getCause()) ||

+                ORACLE_CONNECTION_EXCEPTION.equals(ex.getClass().getName()));

+    }

+

+    private static boolean isCorbaConnectFailure(Throwable ex) {

+        return ((ex instanceof COMM_FAILURE || ex instanceof NO_RESPONSE) &&

+                ((SystemException) ex).completed == CompletionStatus.COMPLETED_NO);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.java
new file mode 100644
index 0000000..9182e73
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.java
@@ -0,0 +1,302 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+import java.lang.reflect.InvocationTargetException;

+import java.lang.reflect.Method;

+import java.rmi.AlreadyBoundException;

+import java.rmi.NotBoundException;

+import java.rmi.Remote;

+import java.rmi.RemoteException;

+import java.rmi.registry.LocateRegistry;

+import java.rmi.registry.Registry;

+import java.rmi.server.UnicastRemoteObject;

+import java.util.ArrayList;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import javassist.CannotCompileException;

+import javassist.ClassPool;

+import javassist.CtClass;

+import javassist.CtNewMethod;

+import javassist.NotFoundException;

+import javassist.bytecode.Descriptor;

+

+import org.springframework.remoting.RemoteAccessException;

+import org.springframework.remoting.rmi.RmiProxyFactoryBean;

+import org.springframework.remoting.rmi.RmiServiceExporter;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.bytecode.ClassGenerator;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;

+

+/**

+ * RmiProtocol.

+ * 

+ * @author qian.lei

+ */

+public class RmiProtocol extends AbstractProtocol {

+

+    public static final int              DEFAULT_PORT = 1099;

+

+    private final Map<Integer, Registry> registryMap  = new ConcurrentHashMap<Integer, Registry>();

+

+    private ProxyFactory                 proxyFactory;

+

+    private RmiProxyFactory              rmiProxyFactory;

+

+    public void setProxyFactory(ProxyFactory proxyFactory) {

+        this.proxyFactory = proxyFactory;

+    }

+

+    public void setRmiProxyFactory(RmiProxyFactory rmiProxyFactory) {

+        this.rmiProxyFactory = rmiProxyFactory;

+    }

+

+    public int getDefaultPort() {

+        return DEFAULT_PORT;

+    }

+

+    @SuppressWarnings("unchecked")

+    public static <T> Class<T> getRemoteClass(Class<T> type) {

+        if (Remote.class.isAssignableFrom(type)) {

+            return type;

+        }

+        try {

+            String remoteType = type.getName() + "$Remote";

+            try {

+                return (Class<T>) Class.forName(remoteType, true, type.getClassLoader());

+            } catch (ClassNotFoundException e) {

+                ClassPool pool = ClassGenerator.getClassPool(type.getClassLoader());

+                CtClass ctClass = pool.makeInterface(remoteType);

+                ctClass.addInterface(getCtClass(pool, Remote.class.getName()));

+                Method[] methods = type.getMethods();

+                for (Method method : methods) {

+                    CtClass[] parameters = new CtClass[method.getParameterTypes().length];

+                    int i = 0;

+                    for (Class<?> pt : method.getParameterTypes()) {

+                        parameters[i++] = getCtClass(pool, pt.getCanonicalName());

+                    }

+                    CtClass[] exceptions = new CtClass[method.getExceptionTypes().length + 1];

+                    exceptions[0] = getCtClass(pool, RemoteException.class.getName());

+                    i = 1;

+                    for (Class<?> et : method.getExceptionTypes()) {

+                        exceptions[i++] = getCtClass(pool, et.getCanonicalName());

+                    }

+                    ctClass.addMethod(CtNewMethod.abstractMethod(

+                            getCtClass(pool, method.getReturnType().getCanonicalName()),

+                            method.getName(), parameters, exceptions, ctClass));

+                }

+                return ctClass.toClass();

+            }

+        } catch (CannotCompileException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        } catch (NotFoundException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+    

+    //fix javassist version problem (getCtClass is since 3.8.5 ,jboss )

+    private static CtClass getCtClass(ClassPool pool, String classname) throws NotFoundException{

+        if (classname.charAt(0) == '[')

+            return Descriptor.toCtClass(classname, pool);

+        else

+            return pool.get(classname);

+    }

+

+    public <T> Exporter<T> export(final Invoker<T> invoker) throws RpcException {

+        if (! Remote.class.isAssignableFrom(invoker.getInterface())

+                && "spring".equals(invoker.getUrl().getParameter("codec", "spring"))) {

+            final RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();

+            rmiServiceExporter.setRegistryPort(invoker.getUrl().getPort());

+            rmiServiceExporter.setServiceName(invoker.getUrl().getPath());

+            rmiServiceExporter.setServiceInterface(invoker.getInterface());

+            rmiServiceExporter.setService(proxyFactory.getProxy(invoker));

+            try {

+                rmiServiceExporter.afterPropertiesSet();

+            } catch (RemoteException e) {

+                throw new RpcException(e.getMessage(), e);

+            }

+            Exporter<T> exporter = new Exporter<T>() {

+                public Invoker<T> getInvoker() {

+                    return invoker;

+                }

+                public void unexport() {

+                    try {

+                        rmiServiceExporter.destroy();

+                    } catch (Throwable e) {

+                        logger.warn(e.getMessage(), e);

+                    }

+                    try {

+                        invoker.destroy();

+                    } catch (Throwable e) {

+                        logger.warn(e.getMessage(), e);

+                    }

+                }

+            };

+            return exporter;

+        } else {

+            Remote remote = rmiProxyFactory.getProxy(invoker);

+            // export.

+            try {

+                UnicastRemoteObject.exportObject(remote, 0);

+            } catch (RemoteException e) {

+                if ("object already exported".equalsIgnoreCase(e.getMessage())) {

+                    logger.warn("Ignore 'object already exported' exception.", e);

+                } else {

+                    throw new RpcException("Export rmi service error.", e);

+                }

+            }

+            // register.

+            Registry registry = getOrCreateRegistry(invoker.getUrl().getPort());

+            try {

+                // bind service.

+                registry.bind(invoker.getUrl().getPath(), remote);

+            } catch (RemoteException e) {

+                throw new RpcException("Bind rmi service [" + invoker.getUrl().getPath() + "] error.",

+                        e);

+            } catch (AlreadyBoundException e) {

+                throw new RpcException("Bind rmi service error. Service name ["

+                        + invoker.getUrl().getPath() + "] already bound.", e);

+            }

+            RmiExporter<T> exporter = new RmiExporter<T>(invoker, remote, registry);

+            exporterMap.put(serviceKey(invoker.getUrl()), exporter);

+            return exporter;

+        }

+    }

+

+    public <T> Invoker<T> refer(final Class<T> serviceType, final URL url) throws RpcException {

+        if (! Remote.class.isAssignableFrom(serviceType)

+                && "spring".equals(url.getParameter("codec", "spring"))) {

+            final RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();

+            rmiProxyFactoryBean.setServiceUrl(url.toIdentityString());

+            rmiProxyFactoryBean.setServiceInterface(serviceType);

+            rmiProxyFactoryBean.setCacheStub(true);

+            rmiProxyFactoryBean.setLookupStubOnStartup(true);

+            rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);

+            rmiProxyFactoryBean.afterPropertiesSet();

+            final Object remoteObject = rmiProxyFactoryBean.getObject();

+            return new Invoker<T>() {

+                public Class<T> getInterface() {

+                    return serviceType;

+                }

+                public URL getUrl() {

+                    return url;

+                }

+                public boolean isAvailable() {

+                    return true;

+                }

+                public Result invoke(Invocation invocation) throws RpcException {

+                    try {

+                        return new RpcResult(remoteObject.getClass().getMethod(invocation.getMethodName(), invocation.getParameterTypes()).invoke(remoteObject, invocation.getArguments()));

+                    } catch (InvocationTargetException e) {

+                        Throwable t = e.getTargetException();

+                        if (t instanceof RemoteAccessException) {

+                            t = ((RemoteAccessException)t).getCause();

+                        }

+                        if (t instanceof RemoteException) {

+                            throw RmiInvoker.setRpcExceptionCode(t, new RpcException("Failed to invoke remote service: " + serviceType + ", method: "

+                                    + invocation.getMethodName() + ", url: " + url + ", cause: " + t.getMessage(), t));

+                        } else {

+                            return new RpcResult(t);

+                        }

+                    } catch (Throwable e) {

+                        if (e instanceof RemoteAccessException) {

+                            e = ((RemoteAccessException)e).getCause();

+                        }

+                        throw RmiInvoker.setRpcExceptionCode(e, new RpcException("Failed to invoke remote service: " + serviceType + ", method: "

+                                + invocation.getMethodName() + ", url: " + url + ", cause: " + e.getMessage(), e));

+                    }

+                }

+                public void destroy() {

+                }

+            };

+        } else {

+            Invoker<T> invoker;

+            try {

+                if ("dubbo".equals(url.getParameter("codec"))) {

+                    RmiProtocol.getRemoteClass(serviceType);

+                }

+                Registry registry = LocateRegistry.getRegistry(url.getHost(), url.getPort());

+                String path = url.getPath();

+                if (path == null || path.length() == 0) {

+                    path = serviceType.getName();

+                }

+                invoker = new RmiInvoker<T>(registry, rmiProxyFactory, rmiProxyFactory.getInvoker(registry.lookup(path), serviceType, url));

+            } catch (RemoteException e) {

+                Throwable cause = e.getCause();

+                boolean isExportedBySpringButNoSpringClass = ClassNotFoundException.class

+                        .isInstance(cause)

+                        && cause.getMessage().contains(

+                                "org.springframework.remoting.rmi.RmiInvocationHandler");

+    

+                String msg = String

+                        .format("Can not create remote object%s. url = %s",

+                                isExportedBySpringButNoSpringClass ? "(Rmi object is exported by spring rmi but NO spring class org.springframework.remoting.rmi.RmiInvocationHandler at consumer side)"

+                                        : "", url);

+                throw new RpcException(msg, e);

+            } catch (NotBoundException e) {

+                throw new RpcException("Rmi service not found. url = " + url, e);

+            }

+            invokers.add(invoker);

+            return invoker;

+        }

+    }

+

+    protected Registry getOrCreateRegistry(int port) {

+        Registry registry = registryMap.get(port);

+        if (registry == null) {

+            try {

+                registry = LocateRegistry.createRegistry(port);

+            } catch (RemoteException e) {

+                throw new IllegalStateException("Failed to create rmi registry on port " + port

+                        + ", cause: " + e.getMessage(), e);

+            }

+            registryMap.put(port, registry);

+        }

+        return registry;

+    }

+

+    public void destroy() {

+        super.destroy();

+        for (Integer key : new ArrayList<Integer>(registryMap.keySet())) {

+            Registry registry = registryMap.remove(key);

+            if (registry != null) {

+                try {

+                    String[] services = registry.list();

+                    if (services != null && services.length > 0) {

+                        for (String service : services) {

+                            if (logger.isInfoEnabled()) {

+                                logger.info("Unbind rmi service: " + service);

+                            }

+                            registry.unbind(service);

+                        }

+                    }

+                } catch (Throwable t) {

+                    logger.warn(t.getMessage(), t);

+                }

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProxyFactory.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProxyFactory.java
new file mode 100644
index 0000000..dea3a24
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProxyFactory.java
@@ -0,0 +1,38 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+import java.rmi.Remote;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.SPI;

+import com.alibaba.dubbo.rpc.Invoker;

+

+/**

+ * RmiProxyFactory

+ * 

+ * @author william.liangf

+ */

+@SPI

+public interface RmiProxyFactory {

+

+    <T> Remote getProxy(Invoker<T> invoker);

+

+    <T> Invoker<T> getInvoker(Remote remote, Class<T> serviceType, URL url);

+

+    boolean isSupported(Remote remote, Class<?> serviceType, URL url);

+    

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/DubboRmiProxyFactory.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/DubboRmiProxyFactory.java
new file mode 100644
index 0000000..6b7a755
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/DubboRmiProxyFactory.java
@@ -0,0 +1,138 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi.proxy;

+

+import java.lang.reflect.Method;

+import java.rmi.Remote;

+import java.rmi.RemoteException;

+import java.rmi.server.RemoteServer;

+import java.rmi.server.ServerNotActiveException;

+

+import javassist.CannotCompileException;

+import javassist.ClassPool;

+import javassist.CtClass;

+import javassist.CtNewMethod;

+import javassist.NotFoundException;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.bytecode.ClassGenerator;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory;

+

+/**

+ * DubboRmiProxyFactory

+ * 

+ * @author william.liangf

+ */

+public class DubboRmiProxyFactory implements RmiProxyFactory {

+    

+    private ProxyFactory proxyFactory;

+

+    public void setProxyFactory(ProxyFactory proxyFactory) {

+        this.proxyFactory = proxyFactory;

+    }

+

+    public boolean isSupported(Remote remote, Class<?> serviceType, URL url) {

+        for (Class<?> i : remote.getClass().getInterfaces()) {

+            if (i.getName().endsWith("$Remote")) {

+                return true;

+            }

+        }

+        return false;

+    }

+

+    public <T> Remote getProxy(final Invoker<T> invoker) {

+        final Class<T> remoteClass = getRemoteClass(invoker.getInterface());

+        return (Remote) proxyFactory.getProxy(new Invoker<T>() {

+            public Class<T> getInterface() {

+                return remoteClass;

+            }

+

+            public URL getUrl() {

+                return invoker.getUrl();

+            }

+

+            public boolean isAvailable() {

+                return true;

+            }

+

+            public Result invoke(Invocation invocation) throws RpcException {

+                String client = null;

+                try {

+                    client = RemoteServer.getClientHost();

+                } catch (ServerNotActiveException e) {

+                    // Ignore it.

+                }

+                RpcContext.getContext().setRemoteAddress(client, 0);

+                return invoker.invoke(invocation);

+            }

+

+            public void destroy() {

+            }

+        });

+    }

+

+    @SuppressWarnings("unchecked")

+    public <T> Invoker<T> getInvoker(Remote remote, Class<T> serviceType, URL url) {

+        return proxyFactory.getInvoker((T) remote, serviceType, url);

+    }

+

+    @SuppressWarnings("unchecked")

+    public static <T> Class<T> getRemoteClass(Class<T> type) {

+        if (Remote.class.isAssignableFrom(type)) {

+            return type;

+        }

+        try {

+            String remoteType = type.getName() + "$Remote";

+            try {

+                return (Class<T>) Class.forName(remoteType, true, type.getClassLoader());

+            } catch (ClassNotFoundException e) {

+                ClassPool pool = ClassGenerator.getClassPool(type.getClassLoader());

+                CtClass ctClass = pool.makeInterface(remoteType);

+                // ctClass.addInterface(pool.getCtClass(type.getName()));

+                ctClass.addInterface(pool.getCtClass(Remote.class.getName()));

+                Method[] methods = type.getMethods();

+                for (Method method : methods) {

+                    CtClass[] parameters = new CtClass[method.getParameterTypes().length];

+                    int i = 0;

+                    for (Class<?> pt : method.getParameterTypes()) {

+                        parameters[i++] = pool.getCtClass(pt.getCanonicalName());

+                    }

+                    CtClass[] exceptions = new CtClass[method.getExceptionTypes().length + 1];

+                    exceptions[0] = pool.getCtClass(RemoteException.class.getName());

+                    i = 1;

+                    for (Class<?> et : method.getExceptionTypes()) {

+                        exceptions[i++] = pool.getCtClass(et.getCanonicalName());

+                    }

+                    ctClass.addMethod(CtNewMethod.abstractMethod(

+                            pool.getCtClass(method.getReturnType().getCanonicalName()),

+                            method.getName(), parameters, exceptions, ctClass));

+                }

+                return ctClass.toClass();

+            }

+        } catch (CannotCompileException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        } catch (NotFoundException e) {

+            throw new IllegalStateException(e.getMessage(), e);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/NativeProxyFactory.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/NativeProxyFactory.java
new file mode 100644
index 0000000..a4612a2
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/NativeProxyFactory.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi.proxy;

+

+import java.rmi.Remote;

+import java.rmi.server.RemoteServer;

+import java.rmi.server.ServerNotActiveException;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory;

+

+/**

+ * DefaultProxyFactoryAdaptive

+ * 

+ * @author william.liangf

+ */

+public class NativeProxyFactory implements RmiProxyFactory {

+

+    private ProxyFactory                      proxyFactory;

+    

+    public void setProxyFactory(ProxyFactory proxyFactory) {

+        this.proxyFactory = proxyFactory;

+    }

+

+    public <T> Remote getProxy(final Invoker<T> invoker) {

+        return (Remote) proxyFactory.getProxy(new Invoker<T>() {

+            public Class<T> getInterface() {

+                return invoker.getInterface();

+            }

+

+            public URL getUrl() {

+                return invoker.getUrl();

+            }

+

+            public boolean isAvailable() {

+                return true;

+            }

+

+            public Result invoke(Invocation invocation) throws RpcException {

+                String client = null;

+                try {

+                    client = RemoteServer.getClientHost();

+                } catch (ServerNotActiveException e) {

+                    // Ignore it.

+                }

+                RpcContext.getContext().setRemoteAddress(client, 0);

+                return invoker.invoke(invocation);

+            }

+

+            public void destroy() {

+            }

+        });

+    }

+

+    public boolean isSupported(Remote remote, Class<?> serviceType, URL url) {

+        return Remote.class.isAssignableFrom(serviceType) && serviceType.isInstance(remote);

+    }

+

+    @SuppressWarnings("unchecked")

+    public <T> Invoker<T> getInvoker(Remote remote, Class<T> serviceType, URL url) {

+        return proxyFactory.getInvoker((T) remote, serviceType, url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/RmiProxyFactoryAdaptive.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/RmiProxyFactoryAdaptive.java
new file mode 100644
index 0000000..23a1e56
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/RmiProxyFactoryAdaptive.java
@@ -0,0 +1,62 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi.proxy;

+

+import java.rmi.Remote;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory;

+

+/**

+ * RmiProxyFactoryAdaptive

+ * 

+ * @author william.liangf

+ */

+@Adaptive

+public class RmiProxyFactoryAdaptive implements RmiProxyFactory {

+

+    public <T> Remote getProxy(Invoker<T> invoker) {

+        ExtensionLoader<RmiProxyFactory> extensionLoader = ExtensionLoader.getExtensionLoader(RmiProxyFactory.class);

+        String name = invoker.getUrl().getParameter("codec", "spring");

+        if (name != null && name.length() > 0 && ! extensionLoader.hasExtension(name)) {

+            throw new IllegalArgumentException("Unsupported protocol codec " + name

+                    + " for protocol RMI, Only support: " + extensionLoader.getSupportedExtensions());

+        }

+        if (Remote.class.isAssignableFrom(invoker.getInterface())) {

+            name = "native";

+        }

+        return extensionLoader.getExtension(name).getProxy(invoker);

+    }

+

+    public <T> Invoker<T> getInvoker(Remote remote, Class<T> serviceType, URL url) {

+        ExtensionLoader<RmiProxyFactory> extensionLoader = ExtensionLoader.getExtensionLoader(RmiProxyFactory.class);

+        for (String name : extensionLoader.getSupportedExtensions()) {

+            RmiProxyFactory rmiProxyFactory = extensionLoader.getExtension(name);

+            if (rmiProxyFactory.isSupported(remote, serviceType, url)) {

+                return rmiProxyFactory.getInvoker(remote, serviceType, url);

+            }

+        }

+        throw new UnsupportedOperationException("Unsupported remote stub " + remote + " by type " + extensionLoader.getSupportedExtensions() + ", service: " + serviceType.getName() + ", url" + url);

+    }

+

+    public boolean isSupported(Remote remote, Class<?> serviceType, URL url) {

+        return true;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/SpringRmiProxyFactory.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/SpringRmiProxyFactory.java
new file mode 100644
index 0000000..8735a64
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/SpringRmiProxyFactory.java
@@ -0,0 +1,124 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi.proxy;

+

+import java.lang.reflect.InvocationTargetException;

+import java.rmi.Remote;

+import java.rmi.RemoteException;

+import java.rmi.server.RemoteServer;

+import java.rmi.server.ServerNotActiveException;

+

+import org.springframework.remoting.rmi.RmiInvocationHandler;

+import org.springframework.remoting.support.RemoteInvocation;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory;

+

+/**

+ * SpringRmiProxyFactory

+ * 

+ * @author william.liangf

+ */

+public class SpringRmiProxyFactory implements RmiProxyFactory {

+

+    public boolean isSupported(Remote remote, Class<?> serviceType, URL url) {

+        return ReflectUtils.isInstance(remote, "org.springframework.remoting.rmi.RmiInvocationHandler");

+    }

+    

+    private static void assertRmiInvocationHandler() {

+        try {

+            Class.forName("org.springframework.remoting.rmi.RmiInvocationHandler");

+        } catch (ClassNotFoundException e1) {

+            throw new RpcException(

+                    "set codec spring for protocol rmi,"

+                            + " but NO spring class org.springframework.remoting.rmi.RmiInvocationHandler at provider side!");

+        }

+    }

+

+    public <T> Remote getProxy(final Invoker<T> invoker) {

+        assertRmiInvocationHandler();

+        return new org.springframework.remoting.rmi.RmiInvocationHandler() {

+            public Object invoke(RemoteInvocation invocation) throws RemoteException,

+                    NoSuchMethodException, IllegalAccessException, InvocationTargetException {

+                String client = null;

+                try {

+                    client = RemoteServer.getClientHost();

+                } catch (ServerNotActiveException e) {

+                    // Ignore it.

+                }

+                Invocation inv = new RpcInvocation(invocation.getMethodName(),

+                        invocation.getParameterTypes(), invocation.getArguments());

+                try {

+                    RpcContext.getContext().setRemoteAddress(client, 0);

+                    return invoker.invoke(inv).recreate();

+                } catch (RpcException e) {

+                    throw new RemoteException(StringUtils.toString(e));

+                } catch (Throwable t) {

+                    throw new InvocationTargetException(t);

+                }

+            }

+            public String getTargetInterfaceName() throws RemoteException {

+                return invoker.getInterface().getName();

+            }

+        };

+    }

+

+    public <T> Invoker<T> getInvoker(final Remote remote, final Class<T> serviceType, final URL url) {

+        assertRmiInvocationHandler();

+        return new Invoker<T>() {

+            public Class<T> getInterface() {

+                return serviceType;

+            }

+

+            public URL getUrl() {

+                return url;

+            }

+

+            public boolean isAvailable() {

+                return true;

+            }

+

+            public Result invoke(Invocation invocation) throws RpcException {

+                RpcResult result = new RpcResult();

+                try {

+                    RemoteInvocation i = new RemoteInvocation();

+                    i.setMethodName(invocation.getMethodName());

+                    i.setParameterTypes(invocation.getParameterTypes());

+                    i.setArguments(invocation.getArguments());

+                    result.setValue(((RmiInvocationHandler) remote).invoke(i));

+                } catch (InvocationTargetException e) {

+                    result.setException(e.getTargetException());

+                } catch (Exception e) {

+                    throw new RpcException(StringUtils.toString(e), e);

+                }

+                return result;

+            }

+

+            public void destroy() {

+            }

+        };

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-rmi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..71466d9
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory b/dubbo-rpc-rmi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory
new file mode 100644
index 0000000..103075e
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory
@@ -0,0 +1,4 @@
+adaptive=com.alibaba.dubbo.rpc.protocol.rmi.proxy.RmiProxyFactoryAdaptive

+native=com.alibaba.dubbo.rpc.protocol.rmi.proxy.NativeProxyFactory

+dubbo=com.alibaba.dubbo.rpc.protocol.rmi.proxy.DubboRmiProxyFactory

+spring=com.alibaba.dubbo.rpc.protocol.rmi.proxy.SpringRmiProxyFactory

diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoService.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoService.java
new file mode 100644
index 0000000..6bcb769
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoService.java
@@ -0,0 +1,43 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+/**

+ * <code>TestService</code>

+ */

+

+public interface DemoService

+{

+	void sayHello(String name);

+

+	String echo(String text);

+

+	long timestamp();

+	

+	void throwTimeout();

+

+	String getThreadName();

+

+	int getSize(String[] strs);

+

+	int getSize(Object[] os);

+

+	Object invoke(String service, String method) throws Exception;

+

+	int stringLength(String str);

+

+	Type enumlength(Type... types);

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoServiceImpl.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoServiceImpl.java
new file mode 100644
index 0000000..5359122
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoServiceImpl.java
@@ -0,0 +1,87 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+import com.alibaba.dubbo.rpc.RpcContext;

+

+/**

+ * DemoServiceImpl

+ */

+

+public class DemoServiceImpl implements DemoService

+{

+	public DemoServiceImpl()

+	{

+		super();

+	}

+

+	public void sayHello(String name) {

+		System.out.println("hello "+name);

+	}

+

+	public String echo(String text)

+	{

+		return text;

+	}

+

+	public long timestamp() {

+		return System.currentTimeMillis();

+	}

+

+	public String getThreadName()

+	{

+		return Thread.currentThread().getName();

+	}

+

+	public int getSize(String[] strs)

+	{

+		if( strs == null )

+			return -1;

+		return strs.length;

+	}

+

+	public int getSize(Object[] os)

+	{

+		if( os == null )

+			return -1;

+		return os.length;

+	}

+

+	public Object invoke(String service, String method) throws Exception

+	{

+		System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());

+		return service + ":" + method;

+	}

+

+	public Type enumlength(Type... types)

+	{

+		if( types.length == 0 )

+			return Type.Lower;

+		return types[0];

+	}

+

+	public int stringLength(String str)

+	{

+		return str.length();

+	}

+

+    public void throwTimeout() {

+        try {

+            Thread.sleep(6000);

+        } catch (InterruptedException e) {

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteService.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteService.java
new file mode 100644
index 0000000..dc7a98c
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteService.java
@@ -0,0 +1,26 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+import java.rmi.Remote;

+import java.rmi.RemoteException;

+

+public interface RemoteService extends Remote

+{

+	String sayHello(String name) throws RemoteException;

+

+	String getThreadName() throws RemoteException;

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java
new file mode 100644
index 0000000..33ab4eb
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+import java.rmi.RemoteException;

+

+import com.alibaba.dubbo.rpc.RpcContext;

+

+public class RemoteServiceImpl implements RemoteService

+{

+	public String getThreadName() throws RemoteException

+	{

+		System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());

+		return Thread.currentThread().getName();

+	}

+

+	public String sayHello(String name) throws RemoteException

+	{

+		return "hello " + name + "@" + RemoteServiceImpl.class.getName();

+	}

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocolTest.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocolTest.java
new file mode 100644
index 0000000..f798fb6
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocolTest.java
@@ -0,0 +1,196 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+

+import static junit.framework.Assert.assertEquals;

+

+import org.junit.Ignore;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.service.EchoService;

+

+public class RmiProtocolTest

+{

+    private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+    private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+    

+    

+    public static interface NonStdRmiInterface {

+        void bark();

+    }

+    

+    @Test

+    public void test_getRemoteClass() throws Exception {

+        Class<NonStdRmiInterface> clazz = RmiProtocol.getRemoteClass(NonStdRmiInterface.class);

+        assertEquals(clazz, RmiProtocol.getRemoteClass(NonStdRmiInterface.class));

+    }

+    

+    @Test

+    public void testRmiProtocolTimeout() throws Exception

+    {

+        System.setProperty("sun.rmi.transport.tcp.responseTimeout", "1000");

+        DemoService service = new DemoServiceImpl();

+        Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));

+        service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));

+        try {

+            try {

+                service.throwTimeout();

+            } catch (RpcException e) {

+                assertEquals(true, e.isTimeout());

+                assertEquals(true, e.getMessage().contains("Read timed out"));

+            }

+        } finally {

+            rpcExporter.unexport();

+        }

+    }

+    

+	@Test

+	public void testRmiProtocol() throws Exception

+	{

+	    {

+    		DemoService service = new DemoServiceImpl();

+    		Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));

+    		

+    		service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));

+    		assertEquals(service.getSize(null), -1);

+    		assertEquals(service.getSize(new String[]{"", "", ""}), 3);

+    		Object result = service.invoke("rmi://127.0.0.1:9001/TestService", "invoke");

+    		assertEquals("rmi://127.0.0.1:9001/TestService:invoke", result);

+    		

+    		rpcExporter.unexport();

+	    }

+

+	    {

+    		RemoteService remoteService = new RemoteServiceImpl();

+    		Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(remoteService, RemoteService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));

+    		

+    		remoteService = proxy.getProxy(protocol.refer(RemoteService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));

+    		remoteService.getThreadName();

+    		for(int i=0;i<100;i++) {

+                String say = remoteService.sayHello("abcd");

+                assertEquals("hello abcd@" + RemoteServiceImpl.class.getName(), say);

+            }

+    		rpcExporter.unexport();

+	    }

+	}

+	

+	// FIXME RMI协议目前的实现不支持转型成 EchoService

+	@Ignore

+	@Test

+	public void testRmiProtocol_echoService() throws Exception

+    {

+	    DemoService service = new DemoServiceImpl();

+	    Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:9002/TestService")));

+        

+	    // cast to EchoService

+        EchoService echo = proxy.getProxy(protocol.refer(EchoService.class, URL.valueOf("rmi://127.0.0.1:9002/TestService")));

+        assertEquals(echo.$echo("test"), "test");

+        assertEquals(echo.$echo("abcdefg"), "abcdefg");

+        assertEquals(echo.$echo(1234), 1234);

+        

+        rpcExporter.unexport();

+        

+        RemoteService remoteService = new RemoteServiceImpl();

+        rpcExporter = protocol.export(proxy.getInvoker(remoteService, RemoteService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));

+        

+        // cast to EchoService

+        echo = proxy.getProxy(protocol.refer(EchoService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));

+        assertEquals(echo.$echo("test"), "test");

+        assertEquals(echo.$echo("abcdefg"), "abcdefg");

+        assertEquals(echo.$echo(1234), 1234);

+        

+        rpcExporter.unexport();

+    }

+

+	/*@Test

+	public void testRpcInvokerGroup() throws Exception

+	{

+		DemoService service = new DemoServiceImpl();

+		RpcUtils.export("demo://127.0.0.1:9030/com.alibaba.dubbo.rpc.TestService",DemoService.class,service);

+		RpcUtils.export("dubbo://127.0.0.1:9031/TestService",DemoService.class,service);

+		RpcUtils.export("rmi://127.0.0.1:9032/com.alibaba.dubbo.rpc.TestService",DemoService.class,service);

+		RpcUtils.export("rmi://127.0.0.1:9033/com.alibaba.dubbo.rpc.TestService",DemoService.class,service);

+

+		service = RpcUtils.createProxy(DemoService.class,

+				new String[]{

+					"demo://127.0.0.1:9030/com.alibaba.dubbo.rpc.TestService?weight=20",

+					"dubbo://127.0.0.1:9031/TestService?weight=20",

+					"rmi://127.0.0.1:9032/com.alibaba.dubbo.rpc.TestService",

+				});

+		assertEquals(service.getSize(null), -1);

+		assertEquals(service.getSize(new String[]{"","",""}), 3);

+

+		// cast to EchoService

+		EchoService echo = RpcUtils.createProxy(EchoService.class,

+				new String[]{

+			"demo://127.0.0.1:9030/com.alibaba.dubbo.rpc.TestService?weight=20",

+			"dubbo://127.0.0.1:9031/TestService?weight=20",

+			"rmi://127.0.0.1:9032/com.alibaba.dubbo.rpc.TestService",

+		});

+		assertEquals(echo.$echo("test"), "test");

+		assertEquals(echo.$echo("abcdefg"), "abcdefg");

+		assertEquals(echo.$echo(1234), 1234);

+	}*/

+

+	/*public void testForkInvoke() throws Exception

+	{

+		DemoService service = new DemoServiceImpl();

+		protocol.export(proxy.createInvoker("dubbo://127.0.0.1:9040/TestService", DemoService.class, service);

+		protocol.export(proxy.createInvoker("dubbo://127.0.0.1:9041/TestService", DemoService.class, service);

+		protocol.export(proxy.createInvoker("rmi://127.0.0.1:9042/com.alibaba.dubbo.rpc.TestService", DemoService.class, service);

+		protocol.export(proxy.createInvoker("rmi://127.0.0.1:9043/com.alibaba.dubbo.rpc.TestService", DemoService.class, service);

+

+		RpcInvokerGroup group = Proxies.createInvoker(DemoService.class, new String[]{

+			"dubbo://127.0.0.1:9040/TestService",

+			"dubbo://127.0.0.1:9041/TestService",

+			"rmi://127.0.0.1:9042/com.alibaba.dubbo.rpc.TestService",

+			"rmi://127.0.0.1:9043/com.alibaba.dubbo.rpc.TestService",

+		});

+		group.getMethodSettings("echo").setFork(true);

+		group.getMethodSettings("echo").setForkInvokeCallback(new ForkInvokeCallback(){

+			public Object merge(RpcInvocation invocation, RpcResult[] results) throws Throwable

+			{

+				System.out.println("merge result begin:");

+				for( RpcResult result : results )

+				{

+					if( result.hasException() )

+						System.out.println("exception:"+result.getException().getMessage());

+					else

+						System.out.println("result:"+result.getResult());

+				}

+				System.out.println("merge result end:");

+				return "aaaa";

+			}

+		});

+

+		service = proxy.createProxy(protocol.refer(DemoService.class, group);

+		service.echo("test");

+

+		// cast to EchoService

+		EchoService echo = proxy.createProxy(protocol.refer(EchoService.class, group);

+		assertEquals(echo.$echo("test"), "test");

+		assertEquals(echo.$echo("abcdefg"), "abcdefg");

+		assertEquals(echo.$echo(1234), 1234);

+	}*/

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/Type.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/Type.java
new file mode 100644
index 0000000..eaa4fda
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/Type.java
@@ -0,0 +1,21 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol.rmi;

+

+public enum Type

+{

+	High, Normal, Lower

+}
\ No newline at end of file
diff --git a/dubbo-rpc/pom.xml b/dubbo-rpc/pom.xml
new file mode 100644
index 0000000..6a26220
--- /dev/null
+++ b/dubbo-rpc/pom.xml
@@ -0,0 +1,38 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>dubbo-parent</artifactId>
+		<version>2.1.1</version>
+	</parent>
+	<artifactId>dubbo-rpc</artifactId>
+	<packaging>jar</packaging>
+	<name>Dubbo RPC Module</name>
+	<description>The rpc module of dubbo project</description>
+	<properties>
+		<skip_maven_deploy>true</skip_maven_deploy>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>dubbo-common</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Exporter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Exporter.java
new file mode 100644
index 0000000..1494390
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Exporter.java
@@ -0,0 +1,44 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;

+

+/**

+ * Exporter. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)

+ * @see com.alibaba.dubbo.rpc.ExporterListener

+ * @see com.alibaba.dubbo.rpc.protocol.AbstractExporter

+ * @author william.liangf

+ */

+public interface Exporter<T> {

+    

+    /**

+     * get invoker.

+     * 

+     * @return invoker

+     */

+    Invoker<T> getInvoker();

+    

+    /**

+     * unexport.

+     * 

+     * <code>

+     *     getInvoker().destroy();

+     * </code>

+     */

+    void unexport();

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.java
new file mode 100644
index 0000000..7c9ec82
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;

+

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * ExporterListener. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@SPI

+public interface ExporterListener {

+

+    /**

+     * The exporter exported.

+     * 

+     * @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)

+     * @param exporter

+     * @throws RpcException

+     */

+    void exported(Exporter<?> exporter) throws RpcException;

+

+    /**

+     * The exporter unexported.

+     * 

+     * @see com.alibaba.dubbo.rpc.Exporter#unexport()

+     * @param exporter

+     * @throws RpcException

+     */

+    void unexported(Exporter<?> exporter);

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Filter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Filter.java
new file mode 100644
index 0000000..e57d6c7
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Filter.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**
+ * Filter. (SPI, Singleton, ThreadSafe)
+ * 
+ * @author william.liangf
+ */

+@SPI

+public interface Filter {
+
+	/**
+	 * do invoke filter.
+	 * 
+	 * <code>

+	 * // before filter
+     * Result result = invoker.invoke(invocation);

+     * // after filter

+     * return result;
+     * </code>
+     * 
+     * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+	 * @param invoker service
+	 * @param invocation invocation.
+	 * @return invoke result.
+	 * @throws RpcException
+	 */

+	Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invocation.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
new file mode 100644
index 0000000..94ca82d
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
@@ -0,0 +1,87 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+import java.util.Map;
+

+/**
+ * Invocation. (API, Prototype, NonThreadSafe)
+ * 
+ * @serial Don't change the class name and package name.
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @see com.alibaba.dubbo.rpc.RpcInvocation
+ * @author qian.lei
+ * @author william.liangf
+ */
+public interface Invocation {

+
+	/**
+	 * get method name.
+	 * 

+	 * @serial
+	 * @return method name.
+	 */
+	String getMethodName();
+
+	/**
+	 * get parameter types.
+	 * 

+	 * @serial
+	 * @return parameter types.
+	 */
+	Class<?>[] getParameterTypes();
+
+	/**
+	 * get arguments.
+	 * 

+	 * @serial
+	 * @return arguments.
+	 */
+	Object[] getArguments();
+
+	/**
+	 * get attachments.
+	 * 

+	 * @serial
+	 * @return attachments.
+	 */
+	Map<String, String> getAttachments();

+	

+	/**

+     * get attachment by key.

+     * 

+     * @serial

+     * @return attachment value.

+     */

+	String getAttachment(String key);

+	

+	/**

+     * get attachment by key with default value.

+     * 

+     * @serial

+     * @return attachment value.

+     */

+	String getAttachment(String key, String defaultValue);

+

+    /**

+     * get the invoker in current context.

+     * 

+     * @transient

+     * @return invoker.

+     */

+    Invoker<?> getInvoker();

+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invoker.java
new file mode 100644
index 0000000..d675bc1
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invoker.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;

+

+import com.alibaba.dubbo.common.Node;

+

+/**

+ * Invoker. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, com.alibaba.dubbo.common.URL)

+ * @see com.alibaba.dubbo.rpc.InvokerListener

+ * @see com.alibaba.dubbo.rpc.protocol.AbstractInvoker

+ * @author william.liangf

+ */

+public interface Invoker<T> extends Node {

+

+    /**

+     * get service interface.

+     * 

+     * @return service interface.

+     */

+    Class<T> getInterface();

+

+    /**

+     * invoke.

+     * 

+     * @param invocation

+     * @return result

+     * @throws RpcException

+     */

+    Result invoke(Invocation invocation) throws RpcException;

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java
new file mode 100644
index 0000000..1200b6a
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * InvokerListener. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@SPI

+public interface InvokerListener {

+

+    /**

+     * The invoker referred

+     * 

+     * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, URL)

+     * @param invoker

+     * @throws RpcException

+     */

+    void referred(Invoker<?> invoker) throws RpcException;

+

+    /**

+     * The invoker destroyed.

+     * 

+     * @see com.alibaba.dubbo.rpc.Invoker#destroy()

+     * @param invoker

+     */

+    void destroyed(Invoker<?> invoker);

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Protocol.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Protocol.java
new file mode 100644
index 0000000..69ff632
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Protocol.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * Protocol. (API/SPI, Singleton, ThreadSafe)
+ * 
+ * @author william.liangf
+ */

+@SPI("dubbo")
+public interface Protocol {
+    
+    /**
+     * get default port.
+     * 
+     * @return default port.
+     */
+    int getDefaultPort();
+
+	/**
+	 * export.
+	 * 
+	 * @param <T>
+	 * @param invoker
+	 * @return exporter
+	 * @throws RpcException
+	 */
+    @Adaptive
+	<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
+
+    /**
+     * refer.
+     * 
+     * @param <T>
+     * @param type
+     * @param url
+     * @return invoker
+     * @throws RpcException
+     */
+    @Adaptive
+    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
+
+	/**
+	 * destory.
+	 */
+	void destroy();
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java
new file mode 100644
index 0000000..2cc798d
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java
@@ -0,0 +1,52 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+
+/**
+ * ProxyFactory. (API/SPI, Singleton, ThreadSafe)
+ * 
+ * @author william.liangf
+ */
+@SPI("javassist")
+public interface ProxyFactory {
+
+    /**
+     * create proxy.
+     * 
+     * @param invoker
+     * @return proxy
+     */
+    @Adaptive({Constants.PROXY_KEY})
+    <T> T getProxy(Invoker<T> invoker) throws RpcException;
+
+    /**
+     * create invoker.
+     * 
+     * @param <T>
+     * @param proxy
+     * @param type
+     * @param url
+     * @return invoker
+     */
+    @Adaptive({Constants.PROXY_KEY})
+    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Result.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Result.java
new file mode 100644
index 0000000..174ab9d
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Result.java
@@ -0,0 +1,73 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+/**
+ * RPC invoke result. (API, Prototype, NonThreadSafe)
+ * 
+ * @serial Don't change the class name and package name.
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @see com.alibaba.dubbo.rpc.RpcResult
+ * @author qianlei
+ * @author william.liangf
+ */
+public interface Result {
+
+	/**
+	 * Get invoke result.
+	 * 
+	 * @return result. if no result return null.
+	 */
+	Object getValue();

+
+	/**
+	 * Get exception.
+	 * 
+	 * @return exception. if no exception return null.
+	 */
+	Throwable getException();
+

+    /**

+     * Has exception.

+     * 

+     * @return has exception.

+     */

+    boolean hasException();

+
+    /**
+     * Recreate.
+     * 
+     * <code>
+     * if (hasException()) {
+     *     throw getException();
+     * } else {
+     *     return getValue();
+     * }
+     * </code>

+     * 

+     * @return result.

+     * @throws if has exception throw it.
+     */
+    Object recreate() throws Throwable;
+

+    /**

+     * @deprecated Replace to getValue()

+     * @see com.alibaba.dubbo.rpc.Result#getValue()

+     */

+    @Deprecated

+    Object getResult();

+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java
new file mode 100644
index 0000000..c1275d5
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java
@@ -0,0 +1,31 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;

+

+import com.alibaba.dubbo.common.Constants;

+

+/**

+ * RpcConstants

+ * 

+ * @deprecated Replace to com.alibaba.dubbo.common.Constants

+ * @author william.liangf

+ */

+@Deprecated

+public final class RpcConstants extends Constants {

+

+    private RpcConstants() {}

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java
new file mode 100644
index 0000000..9ad2edb
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java
@@ -0,0 +1,477 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+import java.net.InetSocketAddress;

+import java.util.Arrays;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.Future;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+
+/**
+ * Thread local context. (API, ThreadLocal, ThreadSafe)
+ * 
+ * @see com.alibaba.dubbo.rpc.filter.ContextFilter
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class RpcContext {
+	
+	private static final ThreadLocal<RpcContext> LOCAL = new ThreadLocal<RpcContext>() {
+		@Override
+		protected RpcContext initialValue() {
+			return new RpcContext();
+		}
+	};
+
+	/**
+	 * get context.
+	 * 
+	 * @return context
+	 */
+	public static RpcContext getContext() {
+	    return LOCAL.get();
+	}
+	
+	/**
+	 * remove context.
+	 * 
+	 * @see com.alibaba.dubbo.rpc.filter.ContextFilter
+	 */
+	public static void removeContext() {
+	    LOCAL.remove();
+	}
+
+    private final Map<String, Object> values = new HashMap<String, Object>();
+    
+    private final Map<String, String> attachments = new HashMap<String, String>();
+

+    private List<Invoker<?>> invokers;

+    
+    private Invoker<?> invoker;
+
+    private Invocation invocation;
+    
+	private InetSocketAddress localAddress;
+
+	private InetSocketAddress remoteAddress;
+	
+	private Future<?> future;
+	
+	protected RpcContext() {
+	}
+
+    /**
+     * is server side.
+     * 
+     * @return server side.
+     */

+	@Deprecated
+    public boolean isServerSide() {
+        return isProviderSide();
+    }

+    

+	/**

+	 * is client side.

+	 * 

+	 * @return client side.

+	 */

+    @Deprecated

+    public boolean isClientSide() {

+        return isConsumerSide();

+    }

+    

+    /**

+     * is provider side.

+     * 

+     * @return provider side.

+     */

+    public boolean isProviderSide() {

+        return ! isConsumerSide();

+    }
+
+    /**
+     * is consumer side.
+     * 
+     * @return consumer side.
+     */
+    public boolean isConsumerSide() {
+        Invoker<?> invoker = getInvoker();
+        if (invoker == null) {
+            return false;
+        }
+        URL url = invoker.getUrl();
+        if (url == null) {
+            return false;
+        }
+        InetSocketAddress address = getRemoteAddress();
+        if (address == null) {
+            return false;
+        }
+        String host;
+        if (address.getAddress() == null) {
+            host = address.getHostName();
+        } else {
+            host = address.getAddress().getHostAddress();
+        }
+        return url.getPort() == address.getPort() && 
+                NetUtils.filterLocalHost(url.getIp()).equals(NetUtils.filterLocalHost(host));
+    }
+

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public List<Invoker<?>> getInvokers() {

+        return invokers == null && invoker != null ? (List)Arrays.asList(invoker) : invokers;

+    }

+

+    public RpcContext setInvokers(List<Invoker<?>> invokers) {

+        this.invokers = invokers;

+        return this;

+    }

+    
+    /**
+     * set current invoker.
+     * 
+     * @param invoker
+     * @return context
+     */
+    public RpcContext setInvoker(Invoker<?> invoker) {
+        this.invoker = invoker;
+        return this;
+    }
+
+    /**
+     * get current invoker.
+     * 
+     * @return invoker
+     */
+    public Invoker<?> getInvoker() {
+        return invoker;
+    }
+    
+    /**
+     * set invocation.
+     * 
+     * @param invocation
+     * @return context
+     */
+    public RpcContext setInvocation(Invocation invocation) {
+        this.invocation = invocation;
+        return this;
+    }
+
+    /**
+     * get invocation.
+     * 
+     * @return invocation
+     */
+    public Invocation getInvocation() {
+        return invocation;
+    }
+
+    /**
+     * set local address.
+     * 
+     * @param address
+     * @return context
+     */
+	public RpcContext setLocalAddress(InetSocketAddress address) {
+	    this.localAddress = address;
+	    return this;
+	}
+
+	/**
+	 * set local address.
+	 * 
+	 * @param host
+	 * @param port
+	 * @return context
+	 */
+    public RpcContext setLocalAddress(String host, int port) {
+        if (port < 0) {
+            port = 0;
+        }
+        this.localAddress = InetSocketAddress.createUnresolved(host, port);
+        return this;
+    }
+
+	/**
+	 * get local address.
+	 * 
+	 * @return local address
+	 */
+	public InetSocketAddress getLocalAddress() {
+		return localAddress;
+	}
+
+	public String getLocalAddressString() {
+        return getLocalHost() + ":" + getLocalPort();
+    }
+    
+	/**
+	 * get local host name.
+	 * 
+	 * @return local host name
+	 */
+	public String getLocalHostName() {
+		String host = localAddress == null ? null : localAddress.getHostName();
+		if (host == null || host.length() == 0) {
+		    return getLocalHost();
+		}
+		return host;
+	}
+
+    /**
+     * set remote address.
+     * 
+     * @param address
+     * @return context
+     */
+    public RpcContext setRemoteAddress(InetSocketAddress address) {
+        this.remoteAddress = address;
+        return this;
+    }
+    
+    /**
+     * set remote address.
+     * 
+     * @param host
+     * @param port
+     * @return context
+     */
+    public RpcContext setRemoteAddress(String host, int port) {
+        if (port < 0) {
+            port = 0;
+        }
+        this.remoteAddress = InetSocketAddress.createUnresolved(host, port);
+        return this;
+    }
+
+	/**
+	 * get remote address.
+	 * 
+	 * @return remote address
+	 */
+	public InetSocketAddress getRemoteAddress() {
+		return remoteAddress;
+	}
+	
+	/**
+	 * get remote address string.
+	 * 
+	 * @return remote address string.
+	 */
+	public String getRemoteAddressString() {
+	    return getRemoteHost() + ":" + getRemotePort();
+	}
+	
+	/**
+	 * get remote host name.
+	 * 
+	 * @return remote host name
+	 */
+	public String getRemoteHostName() {
+		return remoteAddress == null ? null : remoteAddress.getHostName();
+	}
+
+    /**
+     * get local host.
+     * 
+     * @return local host
+     */
+    public String getLocalHost() {
+        String host = localAddress == null ? null : 
+            localAddress.getAddress() == null ? localAddress.getHostName() 

+                    : NetUtils.filterLocalHost(localAddress.getAddress().getHostAddress());
+        if (host == null || host.length() == 0) {
+            return NetUtils.getLocalHost();
+        }
+        return host;
+    }
+
+    /**
+     * get local port.
+     * 
+     * @return port
+     */
+    public int getLocalPort() {
+        return localAddress == null ? 0 : localAddress.getPort();
+    }
+
+    /**
+     * get remote host.
+     * 
+     * @return remote host
+     */
+    public String getRemoteHost() {
+        return remoteAddress == null ? null : 
+            remoteAddress.getAddress() == null ? remoteAddress.getHostName() 

+                    : NetUtils.filterLocalHost(remoteAddress.getAddress().getHostAddress());
+    }
+
+    /**
+     * get remote port.
+     * 
+     * @return remote port
+     */
+    public int getRemotePort() {
+        return remoteAddress == null ? 0 : remoteAddress.getPort();
+    }
+
+    /**
+     * get values.
+     * 
+     * @return values
+     */
+    public Map<String, Object> get() {
+        return values;
+    }
+
+    /**
+     * set values
+     * 
+     * @param values
+     * @return context
+     */
+    public RpcContext set(Map<String, Object> value) {
+        this.values.clear();
+        if (value != null && value.size() > 0) {
+            this.values.putAll(value);
+        }
+        return this;
+    }
+    
+    /**
+     * set value.
+     * 
+     * @param key
+     * @param value
+     * @return context
+     */
+    public RpcContext set(String key, Object value) {
+        if (value == null) {
+            values.remove(key);
+        } else {
+            values.put(key, value);
+        }
+        return this;
+    }
+    
+    /**
+     * remove value.
+     * 
+     * @param key
+     * @return value
+     */
+    public RpcContext remove(String key) {
+        values.remove(key);
+        return this;
+    }
+
+    /**
+     * get value.
+     * 
+     * @param key
+     * @return value
+     */
+    public Object get(String key) {
+        return values.get(key);
+    }
+
+    /**
+     * get attachments.
+     * 
+     * @return attachments
+     */
+    public Map<String, String> getAttachments() {
+        return attachments;
+    }
+
+    /**
+     * set attachments
+     * 
+     * @param attachment
+     * @return context
+     */
+    public RpcContext setAttachments(Map<String, String> attachment) {
+        this.attachments.clear();
+        if (attachment != null && attachment.size() > 0) {
+            this.attachments.putAll(attachment);
+        }
+        return this;
+    }
+    
+    /**
+     * set attachment.
+     * 
+     * @param key
+     * @param value
+     * @return context
+     */
+    public RpcContext setAttachment(String key, String value) {
+        if (value == null) {
+            attachments.remove(key);
+        } else {
+            attachments.put(key, value);
+        }
+        return this;
+    }
+
+    /**
+     * remove attachment.
+     * 
+     * @param key
+     * @return context
+     */
+    public RpcContext removeAttachment(String key) {
+        attachments.remove(key);
+        return this;
+    }
+
+    /**
+     * get attachment.
+     * 
+     * @param key
+     * @return attachment
+     */
+    public String getAttachment(String key) {
+        return attachments.get(key);
+    }
+
+    /**
+     * get future.
+     * 
+     * @param <T>
+     * @return future
+     */
+    @SuppressWarnings("unchecked")
+    public <T> Future<T> getFuture() {
+        return (Future<T>) future;
+    }
+
+    /**
+     * set future.
+     * 
+     * @param future
+     */
+    public void setFuture(Future<?> future) {
+        this.future = future;
+    }

+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcException.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcException.java
new file mode 100644
index 0000000..1ae2295
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcException.java
@@ -0,0 +1,108 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+/**
+ * RPC Exception. (API, Prototype, ThreadSafe)
+ * 
+ * @serial Don't change the class name and properties.
+ * @since 1.0
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @author shawn.qianx
+ * @author william.liangf
+ */
+public final class RpcException extends RuntimeException {
+
+	private static final long serialVersionUID = 7815426752583648734L;
+
+    public static final int UNKNOWN_EXCEPTION = 0;
+    
+    public static final int NETWORK_EXCEPTION = 1;
+    
+    public static final int TIMEOUT_EXCEPTION = 2;
+    
+    public static final int BIZ_EXCEPTION = 3;
+    
+    public static final int FORBIDDEN_EXCEPTION = 4;

+    

+    public static final int SERIALIZATION_EXCEPTION = 5;

+    
+    private int code; // RpcException不能有子类,异常类型用ErrorCode表示,以便保持兼容。
+
+    public RpcException() {
+        super();
+    }
+
+    public RpcException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public RpcException(String message) {
+        super(message);
+    }
+
+    public RpcException(Throwable cause) {
+        super(cause);
+    }
+
+    public RpcException(int code) {
+        super();
+        this.code = code;
+    }
+
+    public RpcException(int code, String message, Throwable cause) {
+        super(message, cause);
+        this.code = code;
+    }
+
+    public RpcException(int code, String message) {
+        super(message);
+        this.code = code;
+    }
+
+    public RpcException(int code, Throwable cause) {
+        super(cause);
+        this.code = code;
+    }
+    
+    public void setCode(int code) {
+        this.code = code;
+    }
+    
+    public int getCode() {
+        return code;
+    }
+    
+    public boolean isBiz() {
+        return code == BIZ_EXCEPTION;
+    }
+    
+    public boolean isForbidded() {
+        return code == FORBIDDEN_EXCEPTION;
+    }
+

+    public boolean isTimeout() {

+        return code == TIMEOUT_EXCEPTION;

+    }

+
+    public boolean isNetwork() {
+        return code == NETWORK_EXCEPTION;
+    }
+

+    public boolean isSerialization() {

+        return code == SERIALIZATION_EXCEPTION;

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java
new file mode 100644
index 0000000..54fe5cd
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * 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.dubbo.rpc;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * RPC Invocation.
+ * 
+ * @serial Don't change the class name and properties.
+ * @author qian.lei
+ */
+public class RpcInvocation implements Invocation, Serializable {
+
+    private static final long serialVersionUID = -4355285085441097045L;
+
+    private String               methodName;
+
+    private Class<?>[]           parameterTypes;
+
+    private Object[]             arguments;
+
+    private Map<String, String>  attachments;
+
+    private transient Invoker<?> invoker;
+
+    public RpcInvocation() {
+    }
+
+    public RpcInvocation(Invocation invocation, Invoker<?> invoker) {
+        this(invocation.getMethodName(), invocation.getParameterTypes(), 
+                invocation.getArguments(), new HashMap<String, String>(invocation.getAttachments()),
+                invocation.getInvoker());
+        if (invoker != null) {
+            URL url = invoker.getUrl();
+            setAttachment(Constants.PATH_KEY, url.getPath());
+            if (url.hasParameter(Constants.INTERFACE_KEY)) {
+                setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));
+            }
+            if (url.hasParameter(Constants.GROUP_KEY)) {
+                setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));
+            }
+            if (url.hasParameter(Constants.VERSION_KEY)) {
+                setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY, "0.0.0"));
+            }
+            if (url.hasParameter(Constants.TIMEOUT_KEY)) {
+                setAttachment(Constants.TIMEOUT_KEY, url.getParameter(Constants.TIMEOUT_KEY));
+            }
+            if (url.hasParameter(Constants.TOKEN_KEY)) {
+                setAttachment(Constants.TOKEN_KEY, url.getParameter(Constants.TOKEN_KEY));
+            }
+            if (url.hasParameter(Constants.APPLICATION_KEY)) {
+                setAttachment(Constants.APPLICATION_KEY, url.getParameter(Constants.APPLICATION_KEY));
+            }
+        }
+    }
+
+    public RpcInvocation(Invocation invocation) {
+        this(invocation.getMethodName(), invocation.getParameterTypes(), 
+                invocation.getArguments(), invocation.getAttachments(), invocation.getInvoker());
+    }
+
+    public RpcInvocation(Method method, Object[] arguments) {
+        this(method.getName(), method.getParameterTypes(), arguments, null, null);
+    }
+
+    public RpcInvocation(Method method, Object[] arguments, Map<String, String> attachment) {
+        this(method.getName(), method.getParameterTypes(), arguments, attachment, null);
+    }
+
+    public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments) {
+        this(methodName, parameterTypes, arguments, null, null);
+    }
+
+    public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments, Map<String, String> attachments) {
+        this(methodName, parameterTypes, arguments, attachments, null);
+    }
+
+    public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments, Map<String, String> attachments, Invoker<?> invoker) {
+        this.methodName = methodName;
+        this.parameterTypes = parameterTypes == null ? new Class<?>[0] : parameterTypes;
+        this.arguments = arguments == null ? new Object[0] : arguments;
+        this.attachments = attachments == null ? new HashMap<String, String>() : attachments;
+        this.invoker = invoker;
+    }
+    
+    public Invoker<?> getInvoker() {
+        return invoker;
+    }
+
+    public void setInvoker(Invoker<?> invoker) {
+        this.invoker = invoker;
+    }
+    
+    public String getMethodName() {
+        return methodName;
+    }
+
+    public Class<?>[] getParameterTypes() {
+        return parameterTypes;
+    }
+
+    public Object[] getArguments() {
+        return arguments;
+    }
+
+    public Map<String, String> getAttachments() {
+        return attachments;
+    }
+
+    public void setMethodName(String methodName) {
+        this.methodName = methodName;
+    }
+
+    public void setParameterTypes(Class<?>[] parameterTypes) {
+        this.parameterTypes = parameterTypes == null ? new Class<?>[0] : parameterTypes;
+    }
+
+    public void setArguments(Object[] arguments) {
+        this.arguments = arguments == null ? new Object[0] : arguments;
+    }
+
+    public void setAttachments(Map<String, String> attachments) {
+        this.attachments = attachments == null ? new HashMap<String, String>() : attachments;
+    }
+    
+    public void setAttachment(String key, String value) {
+        if (attachments == null) {
+            attachments = new HashMap<String, String>();
+        }
+        attachments.put(key, value);
+    }
+
+    public String getAttachment(String key) {
+        if (attachments == null) {
+            return null;
+        }
+        return attachments.get(key);
+    }
+    
+    public String getAttachment(String key, String defaultValue) {
+        if (attachments == null) {
+            return defaultValue;
+        }
+        String value = attachments.get(key);
+        if (value == null || value.length() == 0) {
+            return defaultValue;
+        }
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return "RpcInvocation [methodName=" + methodName + ", parameterTypes="
+                + Arrays.toString(parameterTypes) + ", arguments=" + Arrays.toString(arguments)
+                + ", attachments=" + attachments + "]";
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcResult.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcResult.java
new file mode 100644
index 0000000..3de0d08
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcResult.java
@@ -0,0 +1,94 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+import java.io.Serializable;

+

+/**
+ * RPC Result.
+ * 
+ * @serial Don't change the class name and properties.
+ * @author qianlei
+ */
+public class RpcResult implements Result, Serializable {
+
+    private static final long        serialVersionUID = -6925924956850004727L;
+

+    private Object                   result;
+
+    private Throwable                exception;
+
+    public RpcResult(){
+    }
+
+    public RpcResult(Object result){
+        this.result = result;
+    }
+
+    public RpcResult(Throwable exception){
+        this.exception = exception;
+    }
+
+    public Object recreate() throws Throwable {
+        if (exception != null) {
+            throw exception;
+        }
+        return result;
+    }
+

+    /**

+     * @deprecated Replace to getValue()

+     * @see com.alibaba.dubbo.rpc.RpcResult#getValue()

+     */

+    @Deprecated

+    public Object getResult() {

+        return getValue();

+    }

+

+    /**

+     * @deprecated Replace to setValue()

+     * @see com.alibaba.dubbo.rpc.RpcResult#setValue()

+     */

+    @Deprecated

+    public void setResult(Object result) {

+        setValue(result);

+    }

+
+    public Object getValue() {
+        return result;
+    }
+
+    public void setValue(Object value) {
+        this.result = value;
+    }
+
+    public Throwable getException() {
+        return exception;
+    }
+
+    public void setException(Throwable e) {
+        this.exception = e;
+    }
+
+    public boolean hasException() {
+        return exception != null;
+    }

+

+    @Override

+    public String toString() {

+        return "RpcResult [result=" + result + ", exception=" + exception + "]";

+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java
new file mode 100644
index 0000000..6f51b60
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java
@@ -0,0 +1,321 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;

+

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.atomic.AtomicInteger;

+import java.util.concurrent.atomic.AtomicLong;

+

+import com.alibaba.dubbo.common.URL;

+

+/**

+ * URL statistics. (API, Cached, ThreadSafe)

+ * 

+ * @see com.alibaba.dubbo.rpc.filter.ActiveLimitFilter

+ * @see com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter

+ * @see com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance

+ * @author william.liangf

+ */

+public class RpcStatus {

+

+    private static final ConcurrentMap<String, RpcStatus> SERVICE_STATISTICS = new ConcurrentHashMap<String, RpcStatus>();

+

+    private static final ConcurrentMap<String, ConcurrentMap<String, RpcStatus>> METHOD_STATISTICS = new ConcurrentHashMap<String, ConcurrentMap<String, RpcStatus>>();

+

+    /**

+     * 

+     * @param url

+     * @return status

+     */

+    public static RpcStatus getStatus(URL url) {

+        String uri = url.toIdentityString();

+        RpcStatus status = SERVICE_STATISTICS.get(uri);

+        if (status == null) {

+            SERVICE_STATISTICS.putIfAbsent(uri, new RpcStatus());

+            status = SERVICE_STATISTICS.get(uri);

+        }

+        return status;

+    }

+    

+    /**

+     * 

+     * @param url

+     */

+    public static void removeStatus(URL url) {

+        String uri = url.toIdentityString();

+        SERVICE_STATISTICS.remove(uri);

+    }

+    

+    /**

+     * 

+     * @param url

+     * @param methodName

+     * @return status

+     */

+    public static RpcStatus getStatus(URL url, String methodName) {

+        String uri = url.toIdentityString();

+        ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);

+        if (map == null) {

+            METHOD_STATISTICS.putIfAbsent(uri, new ConcurrentHashMap<String, RpcStatus>());

+            map = METHOD_STATISTICS.get(uri);

+        }

+        RpcStatus status = map.get(methodName);

+        if (status == null) {

+            map.putIfAbsent(methodName, new RpcStatus());

+            status = map.get(methodName);

+        }

+        return status;

+    }

+

+    /**

+     * 

+     * @param url

+     */

+    public static void removeStatus(URL url, String methodName) {

+        String uri = url.toIdentityString();

+        ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);

+        if (map != null) {

+            map.remove(methodName);

+        }

+    }

+

+    /**

+     * 

+     * @param url

+     */

+    public static void beginCount(URL url, String methodName) {

+        beginCount(getStatus(url));

+        beginCount(getStatus(url, methodName));

+    }

+    

+    private static void beginCount(RpcStatus status) {

+        status.active.incrementAndGet();

+    }

+

+    /**

+     * 

+     * @param url

+     * @param elapsed

+     * @param succeeded

+     */

+    public static void endCount(URL url, String methodName, long elapsed, boolean succeeded) {

+        endCount(getStatus(url), elapsed, succeeded);

+        endCount(getStatus(url, methodName), elapsed, succeeded);

+    }

+    

+    private static void endCount(RpcStatus status, long elapsed, boolean succeeded) {

+        status.active.decrementAndGet();

+        status.total.incrementAndGet();

+        status.totalElapsed.addAndGet(elapsed);

+        if (status.maxElapsed.get() < elapsed) {

+            status.maxElapsed.set(elapsed);

+        }

+        if (succeeded) {

+            if (status.succeededMaxElapsed.get() < elapsed) {

+                status.succeededMaxElapsed.set(elapsed);

+            }

+        } else {

+            status.failed.incrementAndGet();

+            status.failedElapsed.addAndGet(elapsed);

+            if (status.failedMaxElapsed.get() < elapsed) {

+                status.failedMaxElapsed.set(elapsed);

+            }

+        }

+    }

+

+    private final ConcurrentMap<String, Object> values = new ConcurrentHashMap<String, Object>();

+

+    private final AtomicInteger active = new AtomicInteger();

+

+    private final AtomicLong total = new AtomicLong();

+

+    private final AtomicInteger failed = new AtomicInteger();

+

+    private final AtomicLong totalElapsed = new AtomicLong();

+

+    private final AtomicLong failedElapsed = new AtomicLong();

+

+    private final AtomicLong maxElapsed = new AtomicLong();

+

+    private final AtomicLong failedMaxElapsed = new AtomicLong();

+

+    private final AtomicLong succeededMaxElapsed = new AtomicLong();

+    

+    private RpcStatus() {}

+

+    /**

+     * set value.

+     * 

+     * @param key

+     * @param value

+     */

+    public void set(String key, Object value) {

+        values.put(key, value);

+    }

+

+    /**

+     * get value.

+     * 

+     * @param key

+     * @return value

+     */

+    public Object get(String key) {

+        return values.get(key);

+    }

+

+    /**

+     * get active.

+     * 

+     * @return active

+     */

+    public int getActive() {

+        return active.get();

+    }

+

+    /**

+     * get total.

+     * 

+     * @return total

+     */

+    public long getTotal() {

+        return total.longValue();

+    }

+    

+    /**

+     * get total elapsed.

+     * 

+     * @return total elapsed

+     */

+    public long getTotalElapsed() {

+        return totalElapsed.get();

+    }

+

+    /**

+     * get average elapsed.

+     * 

+     * @return average elapsed

+     */

+    public long getAverageElapsed() {

+        long total = getTotal();

+        if (total == 0) {

+            return 0;

+        }

+        return getTotalElapsed() / total;

+    }

+

+    /**

+     * get max elapsed.

+     * 

+     * @return max elapsed

+     */

+    public long getMaxElapsed() {

+        return maxElapsed.get();

+    }

+

+    /**

+     * get failed.

+     * 

+     * @return failed

+     */

+    public int getFailed() {

+        return failed.get();

+    }

+

+    /**

+     * get failed elapsed.

+     * 

+     * @return failed elapsed

+     */

+    public long getFailedElapsed() {

+        return failedElapsed.get();

+    }

+

+    /**

+     * get failed average elapsed.

+     * 

+     * @return failed average elapsed

+     */

+    public long getFailedAverageElapsed() {

+        long failed = getFailed();

+        if (failed == 0) {

+            return 0;

+        }

+        return getFailedElapsed() / failed;

+    }

+

+    /**

+     * get failed max elapsed.

+     * 

+     * @return failed max elapsed

+     */

+    public long getFailedMaxElapsed() {

+        return failedMaxElapsed.get();

+    }

+

+    /**

+     * get succeeded.

+     * 

+     * @return succeeded

+     */

+    public long getSucceeded() {

+        return getTotal() - getFailed();

+    }

+

+    /**

+     * get succeeded elapsed.

+     * 

+     * @return succeeded elapsed

+     */

+    public long getSucceededElapsed() {

+        return getTotalElapsed() - getFailedElapsed();

+    }

+

+    /**

+     * get succeeded average elapsed.

+     * 

+     * @return succeeded average elapsed

+     */

+    public long getSucceededAverageElapsed() {

+        long succeeded = getSucceeded();

+        if (succeeded == 0) {

+            return 0;

+        }

+        return getSucceededElapsed() / succeeded;

+    }

+

+    /**

+     * get succeeded max elapsed.

+     * 

+     * @return succeeded max elapsed.

+     */

+    public long getSucceededMaxElapsed() {

+        return succeededMaxElapsed.get();

+    }

+

+    /**

+     * Calculate average TPS (Transaction per second).

+     *

+     * @return tps

+     */

+    public long getAverageTps() {

+        if (getTotalElapsed() >= 1000L) {

+            return getTotal() / (getTotalElapsed() / 1000L);

+        }

+        return getTotal();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/StaticContext.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/StaticContext.java
new file mode 100644
index 0000000..14825b7
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/StaticContext.java
@@ -0,0 +1,72 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+/**
+ * 系统存储,内部类.
+ */
+public class StaticContext extends ConcurrentHashMap<Object, Object>{
+    private static final long serialVersionUID = 1L;
+    private static final String SYSTEMNAME = "system";
+    private String name ;
+	
+	private static final ConcurrentMap<String, StaticContext> context_map = new ConcurrentHashMap<String, StaticContext>() ;
+	
+    private StaticContext(String name) {
+        super();
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+    
+    public static StaticContext getSystemContext() {
+        return getContext(SYSTEMNAME);
+    }
+
+    public static StaticContext getContext(String name) {
+	    StaticContext appContext = context_map.get(name);
+	    if (appContext == null){
+	        appContext = context_map.putIfAbsent(name, new StaticContext(name));
+	        if (appContext == null){
+	            appContext = context_map.get(name);
+	        }
+	    }
+	    return appContext;
+	}
+    public static StaticContext remove(String name){
+        return context_map.remove(name);
+    }
+    
+    public static String getKey(URL url, String methodName, String suffix) {
+        return getKey(url.getServiceKey(), methodName, suffix);
+    }
+    public static String getKey(Map<String, String> paras, String methodName, String suffix) {
+        return getKey(StringUtils.getServiceKey(paras), methodName, suffix);
+    }
+    private static String getKey(String servicekey, String methodName, String suffix) {
+        StringBuffer sb = new StringBuffer().append(servicekey).append(".").append(methodName).append(".").append(suffix);
+        return sb.toString();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/AccessLogFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/AccessLogFilter.java
new file mode 100644
index 0000000..49e4ce9
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/AccessLogFilter.java
@@ -0,0 +1,202 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;
+
+import java.io.File;

+import java.io.FileWriter;

+import java.text.SimpleDateFormat;

+import java.util.Date;

+import java.util.Map;

+import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledExecutorService;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.json.JSON;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.NamedThreadFactory;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+
+/**
+ * 记录Service的Access Log。
+ * <p>
+ * 使用的Logger key是<code><b>dubbo.accesslog</b></code>。
+ * 如果想要配置Access Log只出现在指定的Appender中,可以在Log4j中注意配置上additivity。配置示例:
+ * <code>
+ * <pre>
+ * &lt;logger name="<b>dubbo.accesslog</b>" <font color="red">additivity="false"</font>&gt;
+ *    &lt;level value="info" /&gt;
+ *    &lt;appender-ref ref="foo" /&gt;
+ * &lt;/logger&gt;
+ * </pre></code>
+ * 
+ * @author ding.lid
+ */

+@Activate(group = Constants.PROVIDER, value = Constants.ACCESS_LOG_KEY)
+public class AccessLogFilter implements Filter {
+    
+    private static final Logger logger            = LoggerFactory.getLogger(AccessLogFilter.class);
+
+    private static final String  ACCESS_LOG_KEY   = "dubbo.accesslog";
+    
+    private static final String  FILE_DATE_FORMAT   = "yyyyMMdd";
+
+    private static final String  MESSAGE_DATE_FORMAT   = "yyyy-MM-dd HH:mm:ss";
+
+    private static final int LOG_MAX_BUFFER = 5000;
+
+    private static final long LOG_OUTPUT_INTERVAL = 5000;
+
+    private final ConcurrentMap<String, Set<String>> logQueue = new ConcurrentHashMap<String, Set<String>>();
+
+    private final ScheduledExecutorService logScheduled = Executors.newScheduledThreadPool(2, new NamedThreadFactory("Dubbo-Access-Log", true));
+
+    private volatile ScheduledFuture<?> logFuture = null;
+
+    private class LogTask implements Runnable {
+        public void run() {
+            try {
+                if (logQueue != null && logQueue.size() > 0) {
+                    for (Map.Entry<String, Set<String>> entry : logQueue.entrySet()) {
+                        try {
+                            String accesslog = entry.getKey();
+                            Set<String> logSet = entry.getValue();
+                            File file = new File(accesslog);
+                            File dir = file.getParentFile();
+                            if (null!=dir&&! dir.exists()) {
+                                dir.mkdirs();
+                            }
+                            if (logger.isDebugEnabled()) {
+                                logger.debug("Append log to " + accesslog);
+                            }
+                            if (file.exists()) {
+                                String now = new SimpleDateFormat(FILE_DATE_FORMAT).format(new Date());
+                                String last = new SimpleDateFormat(FILE_DATE_FORMAT).format(new Date(file.lastModified()));
+                                if (! now.equals(last)) {
+                                    File archive = new File(file.getAbsolutePath() + "." + last);
+                                    file.renameTo(archive);
+                                }
+                            }
+                            FileWriter writer = new FileWriter(file, true);
+                            try {
+                                for (String msg : logSet) {
+                                    writer.write(msg);
+                                    writer.write("\r\n");
+                                }
+                                writer.flush();
+                            } finally {
+                                writer.close();
+                            }
+                            logSet.clear();
+                        } catch (Exception e) {
+                            logger.error(e.getMessage(), e);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                logger.error(e.getMessage(), e);
+            }
+        }
+    }
+    
+    private void init() {
+        if (logFuture == null) {
+            synchronized (logScheduled) {
+                if (logFuture == null) {
+                    logFuture = logScheduled.scheduleWithFixedDelay(new LogTask(), LOG_OUTPUT_INTERVAL, LOG_OUTPUT_INTERVAL, TimeUnit.MILLISECONDS);
+                }
+            }
+        }
+    }
+    
+    private void log(String accesslog, String logmessage) {
+        init();
+        Set<String> logSet = logQueue.get(accesslog);
+        if (logSet == null) {
+            logQueue.putIfAbsent(accesslog, new ConcurrentHashSet<String>());
+            logSet = logQueue.get(accesslog);
+        }
+        if (logSet.size() < LOG_MAX_BUFFER) {
+            logSet.add(logmessage);
+        }
+    }
+
+    public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
+        try {
+            String accesslog = invoker.getUrl().getParameter(Constants.ACCESS_LOG_KEY);
+            if (ConfigUtils.isNotEmpty(accesslog)) {
+                RpcContext context = RpcContext.getContext();
+                String serviceName = invoker.getInterface().getName();
+                String version = invoker.getUrl().getParameter(Constants.VERSION_KEY);
+                String group = invoker.getUrl().getParameter(Constants.GROUP_KEY);
+                StringBuilder sn = new StringBuilder();
+                sn.append("[").append(new SimpleDateFormat(MESSAGE_DATE_FORMAT).format(new Date())).append("] ").append(context.getRemoteHost()).append(":").append(context.getRemotePort())
+                .append(" -> ").append(context.getLocalHost()).append(":").append(context.getLocalPort())
+                .append(" - ");
+                if (null != group && group.length() > 0) {
+                    sn.append(group).append("/");
+                }
+                sn.append(serviceName);
+                if (null != version && version.length() > 0) {
+                    sn.append(":").append(version);
+                }
+                sn.append(" ");
+                sn.append(inv.getMethodName());
+                sn.append("(");
+                Class<?>[] types = inv.getParameterTypes();
+                if (types != null && types.length > 0) {
+                    boolean first = true;
+                    for (Class<?> type : types) {
+                        if (first) {
+                            first = false;
+                        } else {
+                            sn.append(",");
+                        }
+                        sn.append(type.getName());
+                    }
+                }
+                sn.append(") ");
+                Object[] args = inv.getArguments();
+                if (args != null && args.length > 0) {
+                    sn.append(JSON.json(args));
+                }
+                String msg = sn.toString();
+                if (ConfigUtils.isDefault(accesslog)) {
+                    LoggerFactory.getLogger(ACCESS_LOG_KEY + "." + invoker.getInterface().getName()).info(msg);
+                } else {
+                    log(accesslog, msg);
+                }
+            }
+        } catch (Throwable t) {
+            logger.warn("Exception in AcessLogFilter of service(" + invoker + " -> " + inv + ")", t);
+        }
+        return invoker.invoke(inv);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilter.java
new file mode 100644
index 0000000..ddd0526
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilter.java
@@ -0,0 +1,87 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcStatus;

+

+/**

+ * LimitInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.CONSUMER, value = Constants.ACTIVES_KEY)

+public class ActiveLimitFilter implements Filter {

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        URL url = invoker.getUrl();

+        String methodName = invocation.getMethodName();

+        int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);

+        RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());

+        if (max > 0) {

+            long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0);

+            long start = System.currentTimeMillis();

+            long remain = timeout;

+            int active = count.getActive();

+            if (active >= max) {

+                synchronized (count) {

+                    active = count.getActive();

+                    while (active >= max) {

+                        try {

+                            count.wait(remain);

+                        } catch (InterruptedException e) {

+                        }

+                        long elapsed = System.currentTimeMillis() - start;

+                        remain = timeout - elapsed;

+                        if (remain <= 0) {

+                            throw new RpcException("Waiting concurrent invoke timeout in client-side for service:  "

+                                                   + invoker.getInterface().getName() + ", method: "

+                                                   + invocation.getMethodName() + ", elapsed: " + elapsed

+                                                   + ", timeout: " + timeout + ". concurrent invokes: " + active

+                                                   + ". max concurrent invoke limit: " + max);

+                        }

+                    }

+                }

+            }

+        }

+        try {

+            long begin = System.currentTimeMillis();

+            RpcStatus.beginCount(url, methodName);

+            try {

+                Result result = invoker.invoke(invocation);

+                RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true);

+                return result;

+            } catch (RuntimeException t) {

+                RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, false);

+                throw t;

+            }

+        } finally {

+            if(max>0){

+                synchronized (count) {

+                    count.notify();

+                } 

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.java
new file mode 100644
index 0000000..db902c3
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.java
@@ -0,0 +1,44 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * ClassLoaderInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.PROVIDER)

+public class ClassLoaderFilter implements Filter {

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        ClassLoader ocl = Thread.currentThread().getContextClassLoader();

+        Thread.currentThread().setContextClassLoader(invoker.getInterface().getClassLoader());

+        try {

+            return invoker.invoke(invocation);

+        } finally {

+            Thread.currentThread().setContextClassLoader(ocl);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.java
new file mode 100644
index 0000000..7a5b5e5
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.java
@@ -0,0 +1,75 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import java.lang.reflect.Method;

+import java.lang.reflect.Type;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.CompatibleTypeUtils;

+import com.alibaba.dubbo.common.utils.PojoUtils;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+

+/**

+ * CompatibleFilter

+ * 

+ * @author william.liangf

+ */

+public class CompatibleFilter implements Filter {

+    

+    private static Logger logger = LoggerFactory.getLogger(CompatibleFilter.class);

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        Result result = invoker.invoke(invocation);

+        if (! invocation.getMethodName().startsWith("$") && ! result.hasException()) {

+            Object value = result.getValue();

+            if (value != null) {

+                try {

+                    Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());

+                    Class<?> type = method.getReturnType();

+                    Object newValue;

+                    String serialization = invoker.getUrl().getParameter(Constants.SERIALIZATION_KEY); 

+                    if ("json".equals(serialization)

+                            || "fastjson".equals(serialization)){

+                        Type gtype = method.getGenericReturnType();

+                        newValue = PojoUtils.realize(value, type, gtype);

+                    } else if (! type.isInstance(value)) {

+                        newValue = PojoUtils.isPojo(type)

+                            ? PojoUtils.realize(value, type) 

+                            : CompatibleTypeUtils.compatibleTypeConvert(value, type);

+                        

+                    } else {

+                        newValue = value;

+                    }

+                    if (newValue != value) {

+                        result = new RpcResult(newValue);

+                    }

+                } catch (Throwable t) {

+                    logger.warn(t.getMessage(), t);

+                }

+            }

+        }

+        return result;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.java
new file mode 100644
index 0000000..c649bc5
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+

+/**

+ * ConsumerContextInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.CONSUMER, order = -10000)

+public class ConsumerContextFilter implements Filter {

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        RpcContext.getContext()

+                .setInvoker(invoker)

+                .setInvocation(invocation)

+                .setLocalAddress(NetUtils.getLocalHost(), 0)

+                .setRemoteAddress(invoker.getUrl().getHost(), 

+                                  invoker.getUrl().getPort());

+        if (invocation instanceof RpcInvocation) {

+            ((RpcInvocation)invocation).setInvoker(invoker);

+        }

+        return invoker.invoke(invocation);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ContextFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ContextFilter.java
new file mode 100644
index 0000000..f373690
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ContextFilter.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import java.util.HashMap;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+

+/**

+ * ContextInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.PROVIDER, order = -10000)

+public class ContextFilter implements Filter {

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        Map<String, String> attachments = invocation.getAttachments();

+        if (attachments != null) {

+            attachments = new HashMap<String, String>(attachments);

+            attachments.remove(Constants.PATH_KEY);

+            attachments.remove(Constants.GROUP_KEY);

+            attachments.remove(Constants.VERSION_KEY);

+            attachments.remove(Constants.DUBBO_VERSION_KEY);

+            attachments.remove(Constants.TOKEN_KEY);

+            attachments.remove(Constants.TIMEOUT_KEY);

+        }

+        RpcContext.getContext()

+                .setInvoker(invoker)

+                .setInvocation(invocation)

+                .setAttachments(attachments)

+                .setLocalAddress(invoker.getUrl().getHost(), 

+                                 invoker.getUrl().getPort());

+        if (invocation instanceof RpcInvocation) {

+            ((RpcInvocation)invocation).setInvoker(invoker);

+        }

+        try {

+            return invoker.invoke(invocation);

+        } finally {

+            RpcContext.removeContext();

+        }

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilter.java
new file mode 100644
index 0000000..3b11ada
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilter.java
@@ -0,0 +1,73 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import java.util.Set;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * DeprecatedInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.CONSUMER, value = Constants.DEPRECATED_KEY)

+public class DeprecatedFilter implements Filter {

+

+    private static final Logger LOGGER = LoggerFactory.getLogger(DeprecatedFilter.class);

+

+    private static final Set<String> logged = new ConcurrentHashSet<String>();

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        String key = invoker.getInterface().getName() + "." + invocation.getMethodName();

+        if (! logged.contains(key)) {

+            logged.add(key);

+            if (invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.DEPRECATED_KEY, false)) {

+                LOGGER.error("The service method " + invoker.getInterface().getName() + "." + getMethodSignature(invocation) + " is DEPRECATED! Declare from " + invoker.getUrl());

+            }

+        }

+        return invoker.invoke(invocation);

+    }

+    

+    private String getMethodSignature(Invocation invocation) {

+        StringBuilder buf = new StringBuilder(invocation.getMethodName());

+        buf.append("(");

+        Class<?>[] types = invocation.getParameterTypes();

+        if (types != null && types.length > 0) {

+            boolean first = true;

+            for (Class<?> type : types) {

+                if (first) {

+                    first = false;

+                } else {

+                    buf.append(", ");

+                }

+                buf.append(type.getSimpleName());

+            }

+        }

+        buf.append(")");

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/EchoFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/EchoFilter.java
new file mode 100644
index 0000000..21be52d
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/EchoFilter.java
@@ -0,0 +1,41 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+

+/**

+ * EchoInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.PROVIDER)

+public class EchoFilter implements Filter {

+

+	public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {

+		if(inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != null && inv.getArguments().length == 1 )

+			return new RpcResult(inv.getArguments()[0]);

+		return invoker.invoke(inv);

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.java
new file mode 100644
index 0000000..05bd448
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.java
@@ -0,0 +1,90 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import java.lang.reflect.Method;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.service.GenericService;

+

+/**

+ * ExceptionInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.PROVIDER)

+public class ExceptionFilter implements Filter {

+

+    private final Logger logger;

+    

+    public ExceptionFilter() {

+        this(LoggerFactory.getLogger(ExceptionFilter.class));

+    }

+    

+    public ExceptionFilter(Logger logger) {

+        this.logger = logger;

+    }

+    

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        try {

+            Result result = invoker.invoke(invocation);

+            if (result.hasException() && GenericService.class != invoker.getInterface()) {

+                try {

+                    Throwable exception = result.getException();

+                    try {

+                        Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());

+                        Class<?>[] exceptionClassses = method.getExceptionTypes();

+                        for (Class<?> exceptionClass : exceptionClassses) {

+                            if (exception.getClass().equals(exceptionClass)) {

+                                return result;

+                            }

+                        }

+                    } catch (NoSuchMethodException e) {

+                        return result;

+                    }

+                    //在package里有类,直接抛出

+                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());

+                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());

+                    if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){

+                        return result;

+                    }

+                    //除上面二种情况外,server端打印日志,并包装成runtimeException抛给客户端

+                    logger.error("Got unchecked and undeclare service method invoke exception: " + exception.getMessage(), exception);

+                    return new RpcResult(new RuntimeException(StringUtils.toString(exception)));

+                } catch (Throwable e) {

+                    logger.warn(e.getMessage(), e);

+                    return result;

+                }

+            }

+            return result;

+        } catch (RuntimeException e) {

+            logger.error("Got unchecked and undeclare service " + invoker.getInterface().getName() + " method " + invocation.getMethodName() + " invoke exception: " + e.getMessage(), e);

+            throw e;

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExecuteLimitFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExecuteLimitFilter.java
new file mode 100644
index 0000000..db4ea24
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExecuteLimitFilter.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcStatus;

+

+/**

+ * ThreadLimitInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY)

+public class ExecuteLimitFilter implements Filter {

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        URL url = invoker.getUrl();

+        String methodName = invocation.getMethodName();

+        int max = url.getMethodParameter(methodName, Constants.EXECUTES_KEY, 0);

+        if (max > 0) {

+            RpcStatus count = RpcStatus.getStatus(url, invocation.getMethodName());

+            if (count.getActive() >= max) {

+                throw new RpcException("Failed to invoke invocation " + invocation + " in provider " + url + ", cause: The service using threads greater than <dubbo:service executes=\"" + max + "\" /> limited.");

+            }

+        }

+        long begin = System.currentTimeMillis();

+        RpcStatus.beginCount(url, methodName);

+        try {

+            Result result = invoker.invoke(invocation);

+            RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true);

+            return result;

+        } catch (RuntimeException t) {

+            RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, false);

+            throw t;

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericFilter.java
new file mode 100644
index 0000000..51d660e
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericFilter.java
@@ -0,0 +1,71 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;
+
+import java.lang.reflect.Method;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.PojoUtils;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.service.GenericException;

+
+/**
+ * GenericInvokerFilter.
+ * 
+ * @author william.liangf
+ */

+@Activate(group = Constants.PROVIDER)
+public class GenericFilter implements Filter {
+    
+    public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
+        if (inv.getMethodName().equals(Constants.$INVOKE) 
+                && inv.getArguments() != null
+                && inv.getArguments().length == 3
+                && ! invoker.getUrl().getParameter(Constants.GENERIC_KEY, false)) {

+            String name = ((String) inv.getArguments()[0]).trim();
+            String[] types = (String[]) inv.getArguments()[1];
+            Object[] args = (Object[]) inv.getArguments()[2];
+            try {
+                Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
+                Class<?>[] params = method.getParameterTypes();
+                if (args == null) {
+                    args = new Object[params.length];
+                }
+                args = PojoUtils.realize(args, params, method.getGenericParameterTypes());
+                Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
+                if (result.hasException()

+                        && ! (result.getException() instanceof GenericException)) {
+                    return new RpcResult(new GenericException(result.getException()));
+                }
+                return new RpcResult(PojoUtils.generalize(result.getValue()));
+            } catch (NoSuchMethodException e) {
+                throw new RpcException(e.getMessage(), e);
+            } catch (ClassNotFoundException e) {
+                throw new RpcException(e.getMessage(), e);
+            }
+        }
+        return invoker.invoke(inv);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.java
new file mode 100644
index 0000000..19ab00e
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.java
@@ -0,0 +1,120 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;
+
+import java.lang.reflect.Constructor;

+import java.lang.reflect.Field;

+import java.lang.reflect.Method;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.PojoUtils;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.service.GenericException;

+
+/**
+ * GenericImplInvokerFilter
+ * 
+ * @author william.liangf
+ */

+@Activate(group = Constants.CONSUMER, value = Constants.GENERIC_KEY)
+public class GenericImplFilter implements Filter {

+    

+    private static final Logger logger = LoggerFactory.getLogger(GenericImplFilter.class);
+
+    private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[] {String.class, String[].class, Object[].class};
+
+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+        if (invoker.getUrl().getParameter(Constants.GENERIC_KEY, false) 

+                && ! Constants.$INVOKE.equals(invocation.getMethodName())
+                && invocation instanceof RpcInvocation) {
+            RpcInvocation invocation2 = (RpcInvocation) invocation;
+            String methodName = invocation2.getMethodName();
+            Class<?>[] parameterTypes = invocation2.getParameterTypes();
+            Object[] arguments = invocation2.getArguments();
+            
+            String[] types = new String[parameterTypes.length];
+            for (int i = 0; i < parameterTypes.length; i ++) {
+                types[i] = ReflectUtils.getName(parameterTypes[i]);
+            }
+            Object[] args = PojoUtils.generalize(arguments);
+            
+            invocation2.setMethodName(Constants.$INVOKE);
+            invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES);
+            invocation2.setArguments(new Object[] {methodName, types, args});
+            Result result = invoker.invoke(invocation2);
+            
+            if (! result.hasException()) {

+                Object value = result.getValue();

+                try {

+                    Method method = invoker.getInterface().getMethod(methodName, parameterTypes);

+                    return new RpcResult(PojoUtils.realize(value, method.getReturnType(), method.getGenericReturnType()));

+                } catch (NoSuchMethodException e) {

+                    throw new RpcException(e.getMessage(), e);

+                }

+            } else if (result.getException() instanceof GenericException) {

+                GenericException exception = (GenericException) result.getException();

+                try {

+                    String className = exception.getExceptionClass();

+                    Class<?> clazz = ReflectUtils.forName(className);

+                    Throwable targetException = null;

+                    Throwable lastException = null;

+                    try {

+                        targetException = (Throwable) clazz.newInstance();

+                    } catch (Throwable e) {

+                        lastException = e;

+                        for (Constructor<?> constructor : clazz.getConstructors()) {

+                            try {

+                                targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]);

+                                break;

+                            } catch (Throwable e1) {

+                                lastException = e1;

+                            }

+                        }

+                    }

+                    if (targetException != null) {

+                        try {

+                            Field field = Throwable.class.getDeclaredField("detailMessage");

+                            if (! field.isAccessible()) {

+                                field.setAccessible(true);

+                            }

+                            field.set(targetException, exception.getExceptionMessage());

+                        } catch (Throwable e) {

+                            logger.warn(e.getMessage(), e);

+                        }

+                        result = new RpcResult(targetException);

+                    } else if (lastException != null) {

+                        throw lastException;

+                    }

+                } catch (Throwable e) {

+                    throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e);

+                }

+            }
+            return result;
+        }
+        return invoker.invoke(invocation);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.java
new file mode 100644
index 0000000..b63ff23
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.java
@@ -0,0 +1,56 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;
+
+import java.util.Arrays;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+
+/**
+ * 如果执行timeout,则log记录下,不干涉服务的运行
+ * 
+ * @author chao.liuc
+ */

+@Activate(group = Constants.PROVIDER)
+public class TimeoutFilter implements Filter {

+

+    private static final Logger logger = LoggerFactory.getLogger(TimeoutFilter.class);

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        long start = System.currentTimeMillis();

+        Result result = invoker.invoke(invocation);

+        long elapsed = System.currentTimeMillis() - start;

+        if (invoker.getUrl() != null

+                && elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(),

+                        "timeout", Integer.MAX_VALUE)) {

+            if (logger.isWarnEnabled()) {

+                logger.warn("invoke time out. method: " + invocation.getMethodName()

+                        + "arguments: " + Arrays.toString(invocation.getArguments()) + " , url is "

+                        + invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");

+            }

+        }

+        return result;

+    }

+    
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TokenFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TokenFilter.java
new file mode 100644
index 0000000..5ff698e
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TokenFilter.java
@@ -0,0 +1,52 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * TokenInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = Constants.PROVIDER, value = Constants.TOKEN_KEY)

+public class TokenFilter implements Filter {

+

+	public Result invoke(Invoker<?> invoker, Invocation inv)

+			throws RpcException {

+	    String token = invoker.getUrl().getParameter(Constants.TOKEN_KEY);

+	    if (ConfigUtils.isNotEmpty(token)) {

+	        Class<?> serviceType = invoker.getInterface();

+	        Map<String, String> attachments = inv.getAttachments();

+    		String remoteToken = attachments == null ? null : attachments.get(Constants.TOKEN_KEY);

+    		if (! token.equals(remoteToken)) {

+    			throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() + "() from consumer " + RpcContext.getContext().getRemoteHost() + " to provider "  + RpcContext.getContext().getLocalHost());

+    		}

+	    }

+		return invoker.invoke(inv);

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilter.java
new file mode 100644
index 0000000..886178e
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ *   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.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcStatus;
+
+/**
+ * 限制 service 或方法的 tps.
+ *
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(group = Constants.PROVIDER, value = Constants.TPS_MAX_KEY)
+public class TpsLimitFilter implements Filter {
+
+// TODO 现在依赖 ActiveLimitFilter 或 ExecuteLimitFilter 的计数,需要放到这两个 filter 后执行
+
+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+
+        long max = invoker.getUrl().getMethodParameter(
+                invocation.getMethodName(), Constants.TPS_MAX_KEY, 0L);
+        // verify method tps
+        if (max > 0L
+                && max <= RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getAverageTps()) {
+            throw new RpcException(
+                    new StringBuilder(64)
+                            .append("Failed to invoke service ")
+                            .append(invoker.getInterface().getName())
+                            .append(".")
+                            .append(invocation.getMethodName())
+                            .append(" because exceed max service tps ")
+                            .append(max).toString());
+        }
+
+        return invoker.invoke(invocation);
+    }
+
+}
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java
new file mode 100644
index 0000000..aecd7f2
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java
@@ -0,0 +1,41 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.listener;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * DeprecatedProtocolFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(Constants.DEPRECATED_KEY)

+public class DeprecatedInvokerListener extends InvokerListenerAdapter {

+

+    private static final Logger LOGGER = LoggerFactory.getLogger(DeprecatedInvokerListener.class);

+

+    public void referred(Invoker<?> invoker) throws RpcException {

+        if (invoker.getUrl().getParameter(Constants.DEPRECATED_KEY, false)) {

+            LOGGER.error("The service " + invoker.getInterface().getName() + " is DEPRECATED! Declare from " + invoker.getUrl());

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ExporterListenerAdapter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ExporterListenerAdapter.java
new file mode 100644
index 0000000..1859ac1
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ExporterListenerAdapter.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.listener;

+

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.ExporterListener;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * ExporterListenerAdapter

+ * 

+ * @author william.liangf

+ */

+public abstract class ExporterListenerAdapter implements ExporterListener {

+

+    public void exported(Exporter<?> exporter) throws RpcException {

+    }

+

+    public void unexported(Exporter<?> exporter) throws RpcException {

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/InvokerListenerAdapter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/InvokerListenerAdapter.java
new file mode 100644
index 0000000..57cfc3c
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/InvokerListenerAdapter.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.listener;

+

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.InvokerListener;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * InvokerListenerAdapter

+ * 

+ * @author william.liangf

+ */

+public abstract class InvokerListenerAdapter implements InvokerListener {

+

+    public void referred(Invoker<?> invoker) throws RpcException {

+    }

+

+    public void destroyed(Invoker<?> invoker) {

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerExporterWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerExporterWrapper.java
new file mode 100644
index 0000000..af37329
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerExporterWrapper.java
@@ -0,0 +1,90 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.listener;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.ExporterListener;

+import com.alibaba.dubbo.rpc.Invoker;

+

+/**

+ * ListenerExporter

+ * 

+ * @author william.liangf

+ */

+public class ListenerExporterWrapper<T> implements Exporter<T> {

+

+    private static final Logger logger = LoggerFactory.getLogger(ListenerExporterWrapper.class);

+

+    private final Exporter<T> exporter;

+    

+    private final List<ExporterListener> listeners;

+

+    public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners){

+        if (exporter == null) {

+            throw new IllegalArgumentException("exporter == null");

+        }

+        this.exporter = exporter;

+        this.listeners = listeners;

+        if (listeners != null && listeners.size() > 0) {

+            RuntimeException exception = null;

+            for (ExporterListener listener : listeners) {

+                if (listener != null) {

+                    try {

+                        listener.exported(this);

+                    } catch (RuntimeException t) {

+                        logger.error(t.getMessage(), t);

+                        exception = t;

+                    }

+                }

+            }

+            if (exception != null) {

+                throw exception;

+            }

+        }

+    }

+

+    public Invoker<T> getInvoker() {

+        return exporter.getInvoker();

+    }

+

+    public void unexport() {

+        try {

+            exporter.unexport();

+        } finally {

+            if (listeners != null && listeners.size() > 0) {

+                RuntimeException exception = null;

+                for (ExporterListener listener : listeners) {

+                    if (listener != null) {

+                        try {

+                            listener.unexported(this);

+                        } catch (RuntimeException t) {

+                            logger.error(t.getMessage(), t);

+                            exception = t;

+                        }

+                    }

+                }

+                if (exception != null) {

+                    throw exception;

+                }

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerInvokerWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerInvokerWrapper.java
new file mode 100644
index 0000000..767c733
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerInvokerWrapper.java
@@ -0,0 +1,100 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.listener;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.InvokerListener;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Result;

+

+/**

+ * ListenerInvoker

+ * 

+ * @author william.liangf

+ */

+public class ListenerInvokerWrapper<T> implements Invoker<T> {

+

+    private static final Logger logger = LoggerFactory.getLogger(ListenerInvokerWrapper.class);

+

+    private final Invoker<T> invoker;

+    

+    private final List<InvokerListener> listeners;

+

+    public ListenerInvokerWrapper(Invoker<T> invoker, List<InvokerListener> listeners){

+        if (invoker == null) {

+            throw new IllegalArgumentException("invoker == null");

+        }

+        this.invoker = invoker;

+        this.listeners = listeners;

+        if (listeners != null && listeners.size() > 0) {

+            for (InvokerListener listener : listeners) {

+                if (listener != null) {

+                    try {

+                        listener.referred(invoker);

+                    } catch (Throwable t) {

+                        logger.error(t.getMessage(), t);

+                    }

+                }

+            }

+        }

+    }

+

+    public Class<T> getInterface() {

+        return invoker.getInterface();

+    }

+

+    public URL getUrl() {

+        return invoker.getUrl();

+    }

+

+    public boolean isAvailable() {

+        return invoker.isAvailable();

+    }

+

+    public Result invoke(Invocation invocation) throws RpcException {

+        return invoker.invoke(invocation);

+    }

+    

+    @Override

+    public String toString() {

+        return getInterface() + " -> " + getUrl()==null?" ":getUrl().toString();

+    }

+

+    public void destroy() {

+        try {

+            invoker.destroy();

+        } finally {

+            if (listeners != null && listeners.size() > 0) {

+                for (InvokerListener listener : listeners) {

+                    if (listener != null) {

+                        try {

+                            listener.destroyed(invoker);

+                        } catch (Throwable t) {

+                            logger.error(t.getMessage(), t);

+                        }

+                    }

+                }

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractExporter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractExporter.java
new file mode 100644
index 0000000..d529354
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractExporter.java
@@ -0,0 +1,63 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+

+/**

+ * AbstractExporter.

+ * 

+ * @author qianlei

+ * @author william.liangf

+ */

+public abstract class AbstractExporter<T> implements Exporter<T> {

+

+    protected final Logger   logger     = LoggerFactory.getLogger(getClass());

+

+    private final Invoker<T> invoker;

+

+    private volatile boolean unexported = false;

+

+    public AbstractExporter(Invoker<T> invoker) {

+        if (invoker == null)

+            throw new IllegalStateException("service invoker == null");

+        if (invoker.getInterface() == null)

+            throw new IllegalStateException("service type == null");

+        if (invoker.getUrl() == null)

+            throw new IllegalStateException("service url == null");

+        this.invoker = invoker;

+    }

+

+    public Invoker<T> getInvoker() {

+        return invoker;

+    }

+

+    public void unexport() {

+        if (unexported) {

+            return ;

+        }

+        unexported = true;

+        getInvoker().destroy();

+    }

+

+    public String toString() {

+        return getInvoker().toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractInvoker.java
new file mode 100644
index 0000000..95ce071
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractInvoker.java
@@ -0,0 +1,165 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.RpcResult;

+
+/**
+ * AbstractInvoker.
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ */
+public abstract class AbstractInvoker<T> implements Invoker<T> {
+
+    protected final Logger   logger    = LoggerFactory.getLogger(getClass());
+
+    private final Class<T>   type;
+
+    private final URL        url;
+
+    private final Map<String, String> attachment;
+
+    private volatile boolean available = true;
+
+    private volatile boolean destroyed = false;
+    
+    public AbstractInvoker(Class<T> type, URL url){
+        this(type, url, (Map<String, String>) null);
+    }
+    
+    public AbstractInvoker(Class<T> type, URL url, String[] keys) {
+        this(type, url, convertAttachment(url, keys));
+    }
+
+    public AbstractInvoker(Class<T> type, URL url, Map<String, String> attachment) {
+        if (type == null)
+            throw new IllegalArgumentException("service type == null");
+        if (url == null)
+            throw new IllegalArgumentException("service url == null");
+        this.type = type;
+        this.url = url;
+        this.attachment = attachment == null ? null : Collections.unmodifiableMap(attachment);
+    }
+    
+    private static Map<String, String> convertAttachment(URL url, String[] keys) {
+        if (keys == null || keys.length == 0) {
+            return null;
+        }
+        Map<String, String> attachment = new HashMap<String, String>();
+        for (String key : keys) {
+            String value = url.getParameter(key);
+            if (value != null && value.length() > 0) {
+                attachment.put(key, value);
+            }
+        }
+        return attachment;
+    }
+
+    public Class<T> getInterface() {
+        return type;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    public boolean isAvailable() {
+        return available;
+    }
+    
+    protected void setAvailable(boolean available) {
+        this.available = available;
+    }
+
+    public void destroy() {
+        if (isDestroyed()) {

+            return;

+        }
+        destroyed = true;
+        setAvailable(false);
+    }

+    

+    public boolean isDestroyed() {

+        return destroyed;

+    }

+

+    public String toString() {
+        return getInterface() + " -> " + getUrl()==null?" ":getUrl().toString();
+    }
+
+    public Result invoke(Invocation inv) throws RpcException {
+        if(destroyed) {
+            throw new RpcException("Rpc invoker for service " + this + " on consumer " + NetUtils.getLocalHost() 
+                                            + " use dubbo version " + Version.getVersion()
+                                            + " is DESTROYED, can not be invoked any more!");

+        }
+        RpcInvocation invocation = (RpcInvocation) inv;

+        invocation.setInvoker(this);
+        Map<String, String> attachments = new HashMap<String, String>();
+        if (attachment != null && attachment.size() > 0) {
+            attachments.putAll(attachment);
+        }
+        Map<String, String> context = RpcContext.getContext().getAttachments();
+        if (context != null) {
+            attachments.putAll(context);
+        }
+        if (invocation.getAttachments() != null) {
+            attachments.putAll(invocation.getAttachments());
+        }
+        invocation.setAttachments(attachments);
+        try {
+            return doInvoke(invocation);
+        } catch (InvocationTargetException e) { // biz exception
+            Throwable te = e.getTargetException();
+            if (te == null) {
+                return new RpcResult(e);
+            } else {
+                if (te instanceof RpcException) {
+                    ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
+                }
+                return new RpcResult(te);
+            }
+        } catch (RpcException e) {
+            if (e.isBiz()) {
+                return new RpcResult(e);
+            } else {
+                throw e;
+            }
+        } catch (Throwable e) {
+            return new RpcResult(e);
+        }
+    }
+
+    protected abstract Result doInvoke(Invocation invocation) throws Throwable;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.java
new file mode 100644
index 0000000..af05c50
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.java
@@ -0,0 +1,118 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+
+/**
+ * abstract ProtocolSupport.
+ * 
+ * @author qian.lei
+ * @author william.liangf
+ */
+public abstract class AbstractProtocol implements Protocol {
+
+	protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+	protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
+

+	//TODO SOFEREFENCE
+    protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();
+    
+	protected static String serviceKey(URL url) {
+	    return serviceKey(url.getPort(), url.getPath(), url.getParameter(Constants.VERSION_KEY),
+                         url.getParameter(Constants.GROUP_KEY));
+	}
+
+	protected static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) {
+		StringBuilder buf = new StringBuilder();
+		if (serviceGroup != null && serviceGroup.length() > 0) {
+			buf.append(serviceGroup);
+			buf.append("/");
+		}
+		buf.append(serviceName);
+		if (serviceVersion != null && serviceVersion.length() > 0 && ! "0.0.0".equals(serviceVersion)) {
+			buf.append(":");
+			buf.append(serviceVersion);
+		}
+		buf.append(":");
+		buf.append(port);
+		return buf.toString();
+	}
+	
+	public void destroy() {
+	    for (Invoker<?> invoker : invokers){
+	        if (invoker != null) {
+	            invokers.remove(invoker);
+                try {
+                    if (logger.isInfoEnabled()) {
+                        logger.info("Destroy reference: " + invoker.getUrl());
+                    }
+                    invoker.destroy();
+                } catch (Throwable t) {
+                    logger.warn(t.getMessage(), t);
+                }
+            }
+	    }
+	    for (String key : new ArrayList<String>(exporterMap.keySet())) {
+            Exporter<?> exporter = exporterMap.remove(key);
+            if (exporter != null) {
+                try {
+                    if (logger.isInfoEnabled()) {
+                        logger.info("Unexport service: " + exporter.getInvoker().getUrl());
+                    }
+                    exporter.unexport();
+                } catch (Throwable t) {
+                    logger.warn(t.getMessage(), t);
+                }
+            }
+        }
+	}
+	@SuppressWarnings("deprecation")
+    protected static int getServerShutdownTimeout() {
+        int timeout = Constants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT;
+        String value = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_KEY);
+        if (value != null && value.length() > 0) {
+            try{
+                timeout = Integer.parseInt(value);
+            }catch (Exception e) {
+            }        
+        } else {
+            value = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_SECONDS_KEY);
+            if (value != null && value.length() > 0) {
+                try{
+                    timeout = Integer.parseInt(value) * 1000;
+                }catch (Exception e) {
+                }        
+            }
+        }
+        
+        return timeout;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/InvokerWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/InvokerWrapper.java
new file mode 100644
index 0000000..4f54dc6
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/InvokerWrapper.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * InvokerWrapper

+ * 

+ * @author william.liangf

+ */

+public class InvokerWrapper<T> implements Invoker<T> {

+    

+    private final Invoker<T> invoker;

+

+    private final URL url;

+

+    public InvokerWrapper(Invoker<T> invoker, URL url){

+        this.invoker = invoker;

+        this.url = url;

+    }

+

+    public Class<T> getInterface() {

+        return invoker.getInterface();

+    }

+

+    public URL getUrl() {

+        return url;

+    }

+

+    public boolean isAvailable() {

+        return invoker.isAvailable();

+    }

+

+    public Result invoke(Invocation invocation) throws RpcException {

+        return invoker.invoke(invocation);

+    }

+

+    public void destroy() {

+        invoker.destroy();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.java
new file mode 100644
index 0000000..ad760f5
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.java
@@ -0,0 +1,108 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol;

+

+import java.util.List;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * ListenerProtocol

+ * 

+ * @author william.liangf

+ */

+public class ProtocolFilterWrapper implements Protocol {

+

+    private final Protocol protocol;

+

+    public ProtocolFilterWrapper(Protocol protocol){

+        if (protocol == null) {

+            throw new IllegalArgumentException("protocol == null");

+        }

+        this.protocol = protocol;

+    }

+

+    public int getDefaultPort() {

+        return protocol.getDefaultPort();

+    }

+

+    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {

+        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {

+            return protocol.export(invoker);

+        }

+        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));

+    }

+

+    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {

+        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {

+            return protocol.refer(type, url);

+        }

+        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);

+    }

+

+    public void destroy() {

+        protocol.destroy();

+    }

+

+    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {

+        Invoker<T> last = invoker;

+        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

+        if (filters.size() > 0) {

+            for (int i = filters.size() - 1; i >= 0; i --) {

+                final Filter filter = filters.get(i);

+                final Invoker<T> next = last;

+                last = new Invoker<T>() {

+

+                    public Class<T> getInterface() {

+                        return invoker.getInterface();

+                    }

+

+                    public URL getUrl() {

+                        return invoker.getUrl();

+                    }

+

+                    public boolean isAvailable() {

+                        return invoker.isAvailable();

+                    }

+

+                    public Result invoke(Invocation invocation) throws RpcException {

+                        return filter.invoke(next, invocation);

+                    }

+

+                    public void destroy() {

+                        invoker.destroy();

+                    }

+

+                    @Override

+                    public String toString() {

+                        return invoker.toString();

+                    }

+                };

+            }

+        }

+        return last;

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.java
new file mode 100644
index 0000000..f10348b
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.java
@@ -0,0 +1,75 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.protocol;

+

+import java.util.Collections;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.ExporterListener;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.InvokerListener;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.listener.ListenerExporterWrapper;

+import com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper;

+

+/**

+ * ListenerProtocol

+ * 

+ * @author william.liangf

+ */

+public class ProtocolListenerWrapper implements Protocol {

+

+    private final Protocol protocol;

+

+    public ProtocolListenerWrapper(Protocol protocol){

+        if (protocol == null) {

+            throw new IllegalArgumentException("protocol == null");

+        }

+        this.protocol = protocol;

+    }

+

+    public int getDefaultPort() {

+        return protocol.getDefaultPort();

+    }

+

+    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {

+        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {

+            return protocol.export(invoker);

+        }

+        return new ListenerExporterWrapper<T>(protocol.export(invoker), 

+                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)

+                        .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));

+    }

+

+    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {

+        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {

+            return protocol.refer(type, url);

+        }

+        return new ListenerInvokerWrapper<T>(protocol.refer(type, url), 

+                Collections.unmodifiableList(

+                        ExtensionLoader.getExtensionLoader(InvokerListener.class)

+                        .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));

+    }

+

+    public void destroy() {

+        protocol.destroy();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyFactory.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyFactory.java
new file mode 100644
index 0000000..3ee576b
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyFactory.java
@@ -0,0 +1,54 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.service.EchoService;

+

+/**

+ * AbstractProxyFactory

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractProxyFactory implements ProxyFactory {

+

+    public <T> T getProxy(Invoker<T> invoker) throws RpcException {

+        Class<?>[] interfaces = null;

+        String config = invoker.getUrl().getParameter("interfaces");

+        if (config != null && config.length() > 0) {

+            String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);

+            if (types != null && types.length > 0) {

+                interfaces = new Class<?>[types.length + 2];

+                interfaces[0] = invoker.getInterface();

+                interfaces[1] = EchoService.class;

+                for (int i = 0; i < types.length; i ++) {

+                    interfaces[i + 1] = ReflectUtils.forName(types[i]);

+                }

+            }

+        }

+        if (interfaces == null) {

+            interfaces = new Class<?>[] {invoker.getInterface(), EchoService.class};

+        }

+        return getProxy(invoker, interfaces);

+    }

+    

+    public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyInvoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyInvoker.java
new file mode 100644
index 0000000..860a550
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyInvoker.java
@@ -0,0 +1,88 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy;
+
+import java.lang.reflect.InvocationTargetException;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * InvokerWrapper
+ * 
+ * @author william.liangf
+ */
+public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
+    
+    private final T proxy;
+    
+    private final Class<T> type;
+    
+    private final URL url;
+
+    public AbstractProxyInvoker(T proxy, Class<T> type, URL url){
+        if (proxy == null) {
+            throw new IllegalArgumentException("proxy == null");
+        }
+        if (type == null) {
+            throw new IllegalArgumentException("interface == null");
+        }
+        if (! type.isInstance(proxy)) {
+            throw new IllegalArgumentException(proxy.getClass().getName() + " not implement interface " + type);
+        }
+        this.proxy = proxy;
+        this.type = type;
+        this.url = url;
+    }
+
+    public Class<T> getInterface() {
+        return type;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public void destroy() {
+    }
+
+    public Result invoke(Invocation invocation) throws RpcException {
+        try {
+            return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
+        } catch (InvocationTargetException e) {
+            return new RpcResult(e.getTargetException());
+        } catch (Throwable e) {
+            throw new RpcException("Failed to invoke remote proxy " + invocation + " to " + getUrl() + ", cause: " + e.getMessage(), e);
+        }
+    }
+    
+    protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;
+
+    @Override
+    public String toString() {
+        return getInterface() + " -> " + getUrl()==null?" ":getUrl().toString();
+    }
+
+    
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/InvokerInvocationHandler.java
new file mode 100644
index 0000000..9e21bec
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/InvokerInvocationHandler.java
@@ -0,0 +1,55 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+/**
+ * InvokerHandler
+ * 
+ * @author william.liangf
+ */
+public class InvokerInvocationHandler implements InvocationHandler {
+
+    private final Invoker<?> invoker;
+    
+    public InvokerInvocationHandler(Invoker<?> handler){
+        this.invoker = handler;
+    }
+
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        String methodName = method.getName();
+        Class<?>[] parameterTypes = method.getParameterTypes();
+        if (method.getDeclaringClass() == Object.class) {
+            return method.invoke(invoker, args);
+        }
+        if ("toString".equals(methodName) && parameterTypes.length == 0) {
+            return invoker.toString();
+        }
+        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
+            return invoker.hashCode();
+        }
+        if ("equals".equals(methodName) && parameterTypes.length == 1) {
+            return invoker.equals(args[0]);
+        }
+        return invoker.invoke(new RpcInvocation(method, args)).recreate();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
new file mode 100644
index 0000000..7e776af
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy.javassist;
+
+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.bytecode.Proxy;

+import com.alibaba.dubbo.common.bytecode.Wrapper;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory;

+import com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker;

+import com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler;

+
+/**
+ * JavaassistRpcProxyFactory 
+
+ * @author william.liangf
+ */
+public class JavassistProxyFactory extends AbstractProxyFactory {
+
+    @SuppressWarnings("unchecked")
+    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
+        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
+    }
+
+    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
+        // TODO Wrapper类不能正确处理带$的类名
+        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
+        return new AbstractProxyInvoker<T>(proxy, type, url) {
+            @Override
+            protected Object doInvoke(T proxy, String methodName, 
+                                      Class<?>[] parameterTypes, 
+                                      Object[] arguments) throws Throwable {
+                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
+            }
+        };
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java
new file mode 100644
index 0000000..afa8054
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy.jdk;
+
+import java.lang.reflect.Method;

+import java.lang.reflect.Proxy;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory;

+import com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker;

+import com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler;

+
+/**
+ * JavaassistRpcProxyFactory
+
+ * @author william.liangf
+ */
+public class JdkProxyFactory extends AbstractProxyFactory {
+
+    @SuppressWarnings("unchecked")
+    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
+        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
+    }
+
+    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
+        return new AbstractProxyInvoker<T>(proxy, type, url) {
+            @Override
+            protected Object doInvoke(T proxy, String methodName, 
+                                      Class<?>[] parameterTypes, 
+                                      Object[] arguments) throws Throwable {
+                Method method = proxy.getClass().getMethod(methodName, parameterTypes);
+                return method.invoke(proxy, arguments);
+            }
+        };
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java
new file mode 100644
index 0000000..23b4378
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java
@@ -0,0 +1,111 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy.wrapper;

+

+import java.lang.reflect.Constructor;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.Version;

+import com.alibaba.dubbo.common.bytecode.Wrapper;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Protocol;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.service.GenericService;

+

+/**

+ * StubProxyFactoryWrapper

+ * 

+ * @author william.liangf

+ */

+public class StubProxyFactoryWrapper implements ProxyFactory {

+    

+    private static final Logger LOGGER = LoggerFactory.getLogger(StubProxyFactoryWrapper.class);

+    

+    private final ProxyFactory proxyFactory;

+    

+    private Protocol protocol;

+    

+    public StubProxyFactoryWrapper(ProxyFactory proxyFactory) {

+        this.proxyFactory = proxyFactory;

+    }

+    

+    public void setProtocol(Protocol protocol) {

+        this.protocol = protocol;

+    }

+

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public <T> T getProxy(Invoker<T> invoker) throws RpcException {

+        T proxy = proxyFactory.getProxy(invoker);

+        if (GenericService.class != invoker.getInterface()) {

+            String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY));

+            if (ConfigUtils.isNotEmpty(stub)) {

+                Class<?> serviceType = invoker.getInterface();

+                if (ConfigUtils.isDefault(stub)) {

+                    if (invoker.getUrl().hasParameter(Constants.STUB_KEY)) {

+                        stub = serviceType.getName() + "Stub";

+                    } else {

+                        stub = serviceType.getName() + "Local";

+                    }

+                }

+                try {

+                    Class<?> stubClass = ReflectUtils.forName(stub);

+                    if (! serviceType.isAssignableFrom(stubClass)) {

+                        throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + serviceType.getName());

+                    }

+                    try {

+                        Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);

+                        proxy = (T) constructor.newInstance(new Object[] {proxy});

+                        //export stub service

+                        URL url = invoker.getUrl();

+                        if (url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT)){

+                            url = url.addParameter(Constants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));

+                            url = url.addParameter(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());

+                            try{

+                                export(proxy, (Class)invoker.getInterface(), url);

+                            }catch (Exception e) {

+                                LOGGER.error("export a stub service error.", e);

+                            }

+                        }

+                    } catch (NoSuchMethodException e) {

+                        throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implemention class " + stubClass.getName(), e);

+                    }

+                } catch (Throwable t) {

+                    LOGGER.error("Failed to create stub implemention class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);

+                    // ignore

+                }

+            }

+        }

+        return proxy;

+    }

+    

+    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {

+        return proxyFactory.getInvoker(proxy, type, url);

+    }

+    

+    private <T> Exporter<T> export(T instance, Class<T> type, URL url) {

+        return protocol.export(proxyFactory.getInvoker(instance, type, url));

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/EchoService.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/EchoService.java
new file mode 100644
index 0000000..c09fcd9
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/EchoService.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.service;
+
+/**
+ * Echo service.
+ * 
+ * @author qian.lei
+ */
+public interface EchoService {
+
+    /**
+     * echo test.
+     * 
+     * @param message message.
+     * @return message.
+     */
+    Object $echo(Object message);
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericException.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericException.java
new file mode 100644
index 0000000..b6c2ebe
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericException.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.service;

+

+import com.alibaba.dubbo.common.utils.StringUtils;

+

+/**

+ * GenericException

+ * 

+ * @serial Don't change the class name and properties.

+ * @author william.liangf

+ */

+public class GenericException extends RuntimeException {

+

+	private static final long serialVersionUID = -1182299763306599962L;

+

+	private String exceptionClass;

+

+    private String exceptionMessage;

+	

+	public GenericException() {

+	}

+

+    public GenericException(String exceptionClass, String exceptionMessage) {

+        super(exceptionMessage);

+        this.exceptionClass = exceptionClass;

+        this.exceptionMessage = exceptionMessage;

+    }

+

+	public GenericException(Throwable cause) {

+		super(StringUtils.toString(cause));

+		this.exceptionClass = cause.getClass().getName();

+		this.exceptionMessage = cause.getMessage();

+	}

+

+	public String getExceptionClass() {

+		return exceptionClass;

+	}

+

+	public void setExceptionClass(String exceptionClass) {

+		this.exceptionClass = exceptionClass;

+	}

+

+	public String getExceptionMessage() {

+		return exceptionMessage;

+	}

+

+	public void setExceptionMessage(String exceptionMessage) {

+		this.exceptionMessage = exceptionMessage;

+	}

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericService.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericService.java
new file mode 100644
index 0000000..bd621b7
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericService.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.service;
+
+/**
+ * 通用服务接口
+ * 
+ * @author william.liangf
+ */
+public interface GenericService {
+
+    /**
+     * 泛化调用
+     * 
+     * @param method 方法名,如:findPerson,如果有重载方法,需带上参数列表,如:findPerson(java.lang.String)
+     * @param parameterTypes 参数类型
+     * @param args 参数列表
+     * @return 返回值
+     * @throws Throwable 方法抛出的异常
+     */
+    Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/DelegateExporter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/DelegateExporter.java
new file mode 100644
index 0000000..5b0eeec
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/DelegateExporter.java
@@ -0,0 +1,45 @@
+/**
+ * Project: dubbo-rpc
+ * 
+ * File Created at 2012-2-24
+ * $Id$
+ * 
+ * Copyright 1999-2100 Alibaba.com Corporation Limited.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Alibaba Company. ("Confidential Information").  You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Alibaba.com.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * DelegateExporter
+ * @author chao.liuc
+ *
+ */
+public class DelegateExporter<T> implements Exporter<T> {
+    
+    private final Exporter<T> exporter;
+    
+    public DelegateExporter(Exporter<T> exporter) {
+        if (exporter == null) {
+            throw new IllegalArgumentException("exporter can not be null");
+        } else {
+            this.exporter = exporter;
+        }
+        
+    }
+    
+    public Invoker<T> getInvoker() {
+        return exporter.getInvoker();
+    }
+    public void unexport() {
+        exporter.unexport();
+    }
+}
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/DelegateInvoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/DelegateInvoker.java
new file mode 100644
index 0000000..016f785
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/DelegateInvoker.java
@@ -0,0 +1,57 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+

+/**

+ * DelegateInvoker

+ * 

+ * @author william.liangf

+ */

+public abstract class DelegateInvoker<T> implements Invoker<T> {

+

+    protected final Invoker<T> invoker;

+

+    public DelegateInvoker(Invoker<T> invoker) {

+        this.invoker = invoker;

+    }

+

+    public Class<T> getInterface() {

+        return invoker.getInterface();

+    }

+

+    public URL getUrl() {

+        return invoker.getUrl();

+    }

+

+    public boolean isAvailable() {

+        return invoker.isAvailable();

+    }

+

+    public Result invoke(Invocation invocation) throws RpcException {

+        return invoker.invoke(invocation);

+    }

+

+    public void destroy() {

+        invoker.destroy();

+    }

+

+}

diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
new file mode 100644
index 0000000..9ecb9f6
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
@@ -0,0 +1,222 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;

+

+import java.lang.reflect.Constructor;

+import java.lang.reflect.Type;

+import java.util.List;

+import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+import com.alibaba.dubbo.common.json.JSON;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.common.utils.PojoUtils;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.common.utils.StringUtils;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.ProxyFactory;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.RpcResult;

+

+/**

+ * @author chao.liuc

+ * @author william.liangf

+ * 

+ */

+final public class MockInvoker<T> implements Invoker<T> {

+	private final static ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+    private final static Map<String, Invoker<?>> mocks = new ConcurrentHashMap<String, Invoker<?>>();

+    private final static Map<String, Throwable> throwables = new ConcurrentHashMap<String, Throwable>();

+    

+    private final URL url ;

+    

+    public MockInvoker(URL url) {

+        this.url = url;

+    }

+	public Result invoke(Invocation invocation) throws RpcException {

+    	String mock = getUrl().getParameter(invocation.getMethodName()+"."+Constants.MOCK_KEY);

+    	if (invocation instanceof RpcInvocation) {

+    		((RpcInvocation) invocation).setInvoker(this);

+    	}

+    	if (StringUtils.isBlank(mock)){

+    		mock = getUrl().getParameter(Constants.MOCK_KEY);

+    	}

+    	

+    	if (StringUtils.isBlank(mock)){

+    		throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));

+    	}

+        mock = normallizeMock(URL.decode(mock));

+        if (Constants.RETURN_PREFIX.trim().equalsIgnoreCase(mock.trim())){

+        	RpcResult result = new RpcResult();

+        	result.setValue(null);

+        	return result;

+        } else if (mock.startsWith(Constants.RETURN_PREFIX)) {

+            mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();

+            mock = mock.replace('`', '"');

+            try {

+                Type[] returnTypes = RpcUtils.getReturnTypes(invocation);

+                Object value = parseMockValue(mock, returnTypes);

+                return new RpcResult(value);

+            } catch (Exception ew) {

+            	throw new RpcException("mock return invoke error .invocation :" + invocation + ", mock:" + mock + ", url: "+ url , ew);

+            }

+        } else if (mock.startsWith(Constants.THROW_PREFIX)) {

+        	mock = mock.substring(Constants.THROW_PREFIX.length()).trim();

+            mock = mock.replace('`', '"');

+            if (StringUtils.isBlank(mock)){

+            	throw new RpcException(" mocked exception for Service degradation. ");

+            } else { //用户自定义类

+            	Throwable t = getThrowable(mock);

+				throw new RpcException(RpcException.BIZ_EXCEPTION, t);

+            }

+        } else { //impl mock

+             try {

+                 Invoker<T> invoker = getInvoker(mock);

+                 return invoker.invoke(invocation);

+             } catch (Throwable t) {

+                 throw new RpcException("Failed to create mock implemention class " + mock , t);

+             }

+        }

+    }

+    

+	private Throwable getThrowable(String throwstr){

+    	Throwable throwable =(Throwable) throwables.get(throwstr);

+		if (throwable != null ){

+			return throwable;

+		} else {

+			Throwable t = null;

+			try {

+				Class<?> bizException = ReflectUtils.forName(throwstr);

+            	Constructor<?> constructor;

+				constructor = ReflectUtils.findConstructor(bizException, String.class);

+				t = (Throwable) constructor.newInstance(new Object[] {" mocked exception for Service degradation. "});

+				if (throwables.size() < 1000) {

+					throwables.put(throwstr, t);	

+				}

+			} catch (Exception e) {

+				throw new RpcException("mock throw error :" + throwstr + " argument error.", e);

+			}

+			return t;

+		}

+    }

+    

+    @SuppressWarnings("unchecked")

+	private Invoker<T> getInvoker(String mockService){

+    	Invoker<T> invoker =(Invoker<T>) mocks.get(mockService);

+		if (invoker != null ){

+			return invoker;

+		} else {

+       	 	Class<T> serviceType = (Class<T>)ReflectUtils.forName(url.getServiceInterface());

+            if (ConfigUtils.isDefault(mockService)) {

+            	mockService = serviceType.getName() + "Mock";

+            }

+            

+            Class<?> mockClass = ReflectUtils.forName(mockService);

+            if (! serviceType.isAssignableFrom(mockClass)) {

+                throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());

+            }

+			

+            if (! serviceType.isAssignableFrom(mockClass)) {

+                throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());

+            }

+            try {

+                T mockObject = (T) mockClass.newInstance();

+                invoker = proxyFactory.getInvoker(mockObject, (Class<T>)serviceType, url);

+                if (mocks.size() < 10000) {

+                	mocks.put(mockService, invoker);

+                }

+                return invoker;

+            } catch (InstantiationException e) {

+                throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e);

+            } catch (IllegalAccessException e) {

+				throw new IllegalStateException(e);

+			}

+		}

+    }

+    //mock=fail:throw

+    //mock=fail:return

+    //mock=xx.Service

+    private String normallizeMock(String mock) {

+    	if (mock == null || mock.trim().length() ==0){

+    		return mock;

+    	} else if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock.trim()) || "force".equalsIgnoreCase(mock.trim())){

+    		mock = url.getServiceInterface()+"Mock";

+    	}

+    	if (mock.startsWith(Constants.FAIL_PREFIX)) {

+            mock = mock.substring(Constants.FAIL_PREFIX.length()).trim();

+        } else if (mock.startsWith(Constants.FORCE_PREFIX)) {

+            mock = mock.substring(Constants.FORCE_PREFIX.length()).trim();

+        }

+    	return mock;

+    }

+    

+    public static Object parseMockValue(String mock) throws Exception {

+        return parseMockValue(mock, null);

+    }

+    

+    public static Object parseMockValue(String mock, Type[] returnTypes) throws Exception {

+        Object value = null;

+        if ("empty".equals(mock)) {

+            value = ReflectUtils.getEmptyObject(returnTypes != null && returnTypes.length > 0 ? (Class<?>)returnTypes[0] : null);

+        } else if ("null".equals(mock)) {

+            value = null;

+        } else if ("true".equals(mock)) {

+            value = true;

+        } else if ("false".equals(mock)) {

+            value = false;

+        } else if (mock.length() >=2 && (mock.startsWith("\"") && mock.endsWith("\"")

+                || mock.startsWith("\'") && mock.endsWith("\'"))) {

+            value = mock.subSequence(1, mock.length() - 1);

+        } else if (returnTypes !=null && returnTypes.length >0 && returnTypes[0] == String.class) {

+            value = mock;

+        } else if (StringUtils.isNumeric(mock)) {

+            value = JSON.parse(mock);

+        }else if (mock.startsWith("{")) {

+            value = JSON.parse(mock, Map.class);

+        } else if (mock.startsWith("[")) {

+            value = JSON.parse(mock, List.class);

+        } else {

+            value = mock;

+        }

+        if (returnTypes != null && returnTypes.length > 0) {

+            value = PojoUtils.realize(value, (Class<?>)returnTypes[0], returnTypes.length > 1 ? returnTypes[1] : null);

+        }

+        return value;

+    }

+    

+	public URL getUrl() {

+		return this.url;

+	}

+

+	public boolean isAvailable() {

+		return true;

+	}

+

+	public void destroy() {

+		//do nothing

+	}

+

+	public Class<T> getInterface() {

+		//FIXME

+		return null;

+	}

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/MockProtocol.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/MockProtocol.java
new file mode 100644
index 0000000..fe23c24
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/MockProtocol.java
@@ -0,0 +1,42 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Exporter;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;

+

+/**

+ * MockProtocol 用于在consumer side 通过url及类型生成一个mockInvoker

+ * @author chao.liuc

+ *

+ */

+final public class MockProtocol extends AbstractProtocol {

+

+	public int getDefaultPort() {

+		return 0;

+	}

+

+	public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {

+		throw new UnsupportedOperationException();

+	}

+

+	public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {

+		return new MockInvoker<T>(url);

+	}

+}

diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java
new file mode 100644
index 0000000..31f2647
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java
@@ -0,0 +1,77 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;

+

+import java.lang.reflect.Method;

+import java.lang.reflect.Type;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.rpc.Invocation;

+

+/**

+ * RpcUtils

+ * 

+ * @author william.liangf

+ */

+public class RpcUtils {

+

+    private static final Logger logger = LoggerFactory.getLogger(RpcUtils.class);

+

+    public static Class<?> getReturnType(Invocation invocation) {

+        try {

+            if (invocation != null && invocation.getInvoker() != null

+                    && invocation.getInvoker().getUrl() != null

+                    && ! invocation.getMethodName().startsWith("$")) {

+                String service = invocation.getInvoker().getUrl().getServiceInterface();

+                if (service != null && service.length() > 0) {

+                    Class<?> cls = ReflectUtils.forName(service);

+                    Method method = cls.getMethod(invocation.getMethodName(), invocation.getParameterTypes());

+                    if (method.getReturnType() == void.class) {

+                        return null;

+                    }

+                    return method.getReturnType();

+                }

+            }

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+        return null;

+    }

+

+    public static Type[] getReturnTypes(Invocation invocation) {

+        try {

+            if (invocation != null && invocation.getInvoker() != null

+                    && invocation.getInvoker().getUrl() != null

+                    && ! invocation.getMethodName().startsWith("$")) {

+                String service = invocation.getInvoker().getUrl().getServiceInterface();

+                if (service != null && service.length() > 0) {

+                    Class<?> cls = ReflectUtils.forName(service);

+                    Method method = cls.getMethod(invocation.getMethodName(), invocation.getParameterTypes());

+                    if (method.getReturnType() == void.class) {

+                        return null;

+                    }

+                    return new Type[]{method.getReturnType(), method.getGenericReturnType()};

+                }

+            }

+        } catch (Throwable t) {

+            logger.warn(t.getMessage(), t);

+        }

+        return null;

+    }

+

+}

diff --git a/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..2689465
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1,14 @@
+echo=com.alibaba.dubbo.rpc.filter.EchoFilter

+generic=com.alibaba.dubbo.rpc.filter.GenericFilter

+genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter

+token=com.alibaba.dubbo.rpc.filter.TokenFilter

+accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter

+activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter

+classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter

+context=com.alibaba.dubbo.rpc.filter.ContextFilter

+consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter

+exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter

+executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter

+deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter

+compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter

+timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener b/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener
new file mode 100644
index 0000000..6ec09b5
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener
@@ -0,0 +1 @@
+deprecated=com.alibaba.dubbo.rpc.listener.DeprecatedInvokerListener
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..6d823d6
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1,3 @@
+filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
+listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
+mock=com.alibaba.dubbo.rpc.support.MockProtocol
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory b/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory
new file mode 100644
index 0000000..d281b8a
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory
@@ -0,0 +1,3 @@
+stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper

+jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory

+javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/CustomArgument.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/CustomArgument.java
new file mode 100644
index 0000000..fc8af58
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/CustomArgument.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+import java.io.Serializable;
+
+import com.alibaba.dubbo.rpc.support.Type;
+
+/**
+ * @author chao.liuc
+ *
+ */
+@SuppressWarnings("serial")
+public class CustomArgument implements Serializable{
+    
+    public CustomArgument(){}
+    
+    public CustomArgument(Type type, String name) {
+        super();
+        this.type = type;
+        this.name = name;
+    }
+    Type type;
+    String name;
+    public Type getType() {
+        return type;
+    }
+    public void setType(Type type) {
+        this.type = type;
+    }
+    public String getName() {
+        return name;
+    }
+    public void setName(String name) {
+        this.name = name;
+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/DemoRequest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/DemoRequest.java
new file mode 100644
index 0000000..665180d
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/DemoRequest.java
@@ -0,0 +1,64 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;
+
+import java.io.Serializable;
+
+/**
+ * TestRequest.
+ * 
+ * @author qian.lei
+ */
+
+class DemoRequest implements Serializable
+{
+	private static final long serialVersionUID = -2579095288792344869L;
+
+	private String mServiceName;
+	
+	private String mMethodName;
+
+	private Class<?>[] mParameterTypes;
+
+	private Object[] mArguments;
+
+	public DemoRequest(String serviceName,String methodName, Class<?>[] parameterTypes,Object[] args)
+	{
+		mServiceName = serviceName;
+		mMethodName = methodName;
+		mParameterTypes = parameterTypes;
+		mArguments = args;
+	}
+
+	public String getServiceName()
+	{
+		return mServiceName;
+	}
+
+	public String getMethodName()
+	{
+		return mMethodName;
+	}
+
+	public Class<?>[] getParameterTypes() {
+		return mParameterTypes;
+	}
+
+	public Object[] getArguments()
+	{
+		return mArguments;
+	}
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/ProtocolUtils.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/ProtocolUtils.java
new file mode 100644
index 0000000..73f5d11
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/ProtocolUtils.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.ExtensionLoader;

+

+/**

+ * TODO Comment of ProtocolUtils

+ * @author william.liangf

+ *

+ */

+public class ProtocolUtils {

+

+    private static Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

+    private static ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

+    

+    public static <T> T refer(Class<T> type, String url) {

+        return refer(type, URL.valueOf(url));

+    }

+    

+    public static <T> T refer(Class<T> type, URL url) {

+        return proxy.getProxy(protocol.refer(type, url));

+    }

+    

+    public static <T> Exporter<T> export(T instance, Class<T> type, String url) {

+        return export(instance, type, URL.valueOf(url));

+    }

+    

+    public static <T> Exporter<T> export(T instance, Class<T> type, URL url) {

+        return protocol.export(proxy.getInvoker(instance, type, url));

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/AccessLogFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/AccessLogFilterTest.java
new file mode 100644
index 0000000..26cd544
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/AccessLogFilterTest.java
@@ -0,0 +1,67 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import static org.junit.Assert.assertEquals;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.LogUtil;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.support.MockInvocation;

+import com.alibaba.dubbo.rpc.support.MyInvoker;

+

+/**

+ * AccessLogFilterTest.java

+ * 

+ * @author tony.chenl

+ */

+public class AccessLogFilterTest {

+

+    Filter accessLogFilter = new AccessLogFilter();

+

+    // 测试filter不会抛出异常

+    @Test

+    public void testInvokeException() {

+        Invoker<AccessLogFilterTest> invoker = new MyInvoker<AccessLogFilterTest>(null);

+        Invocation invocation = new MockInvocation();

+        LogUtil.start();

+        accessLogFilter.invoke(invoker, invocation);

+        assertEquals(1, LogUtil.findMessage("Exception in AcessLogFilter of service"));

+        LogUtil.stop();

+    }

+

+    // TODO how to assert thread action

+    @Test

+    public void testDefault() {

+        URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1");

+        Invoker<AccessLogFilterTest> invoker = new MyInvoker<AccessLogFilterTest>(url);

+        Invocation invocation = new MockInvocation();

+        accessLogFilter.invoke(invoker, invocation);

+    }

+

+    @Test

+    public void testCustom() {

+        URL url = URL.valueOf("test://test:11/test?accesslog=alibaba");

+        Invoker<AccessLogFilterTest> invoker = new MyInvoker<AccessLogFilterTest>(url);

+        Invocation invocation = new MockInvocation();

+        accessLogFilter.invoke(invoker, invocation);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilterTest.java
new file mode 100644
index 0000000..4fa5d68
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilterTest.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import static org.junit.Assert.assertNotSame;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.support.MockInvocation;

+import com.alibaba.dubbo.rpc.support.MyInvoker;

+

+/**

+ * ActiveLimitFilterTest.java

+ * 

+ * @author tony.chenl

+ */

+public class ActiveLimitFilterTest {

+

+    Filter                      activeLimitFilter = new ActiveLimitFilter();

+    private static volatile int count             = 0;

+

+    @Test

+    public void testInvokeNoActives() {

+        URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&actives=0");

+        Invoker<ActiveLimitFilterTest> invoker = new MyInvoker<ActiveLimitFilterTest>(url);

+        Invocation invocation = new MockInvocation();

+        activeLimitFilter.invoke(invoker, invocation);

+    }

+

+    @Test

+    public void testInvokeLessActives() {

+        URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&actives=10");

+        Invoker<ActiveLimitFilterTest> invoker = new MyInvoker<ActiveLimitFilterTest>(url);

+        Invocation invocation = new MockInvocation();

+        activeLimitFilter.invoke(invoker, invocation);

+    }

+

+    @Test

+    public void testInvokeGreaterActives() {

+        URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&actives=1&timeout=1");

+        final Invoker<ActiveLimitFilterTest> invoker = new MyInvoker<ActiveLimitFilterTest>(url);

+        final Invocation invocation = new MockInvocation();

+        for (int i = 0; i < 100; i++) {

+            Thread thread = new Thread(new Runnable() {

+

+                public void run() {

+                    for (int i = 0; i < 100; i++) {

+                        try {

+                            activeLimitFilter.invoke(invoker, invocation);

+                        } catch (RpcException expected) {

+                            count++;

+                        }

+                    }

+                }

+            });

+            thread.start();

+        }

+        try {

+            Thread.sleep(1000);

+        } catch (InterruptedException e) {

+            e.printStackTrace();

+        }

+        assertNotSame(0, count);

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/CompatibleFilterFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/CompatibleFilterFilterTest.java
new file mode 100644
index 0000000..87532e7
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/CompatibleFilterFilterTest.java
@@ -0,0 +1,170 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import static org.junit.Assert.assertArrayEquals;

+import static org.junit.Assert.assertEquals;

+

+import org.easymock.EasyMock;

+import org.junit.After;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.support.DemoService;

+import com.alibaba.dubbo.rpc.support.Type;

+

+/**

+ * CompatibleFilterTest.java

+ * 

+ * @author tony.chenl

+ */

+public class CompatibleFilterFilterTest {

+

+    Filter     compatibleFilter = new CompatibleFilter();

+    Invocation invocation;

+    Invoker<DemoService>    invoker;

+

+    @After

+    public void tearDown() {

+        EasyMock.reset(invocation, invoker);

+    }

+

+    @Test

+    public void testInvokerGeneric() {

+        invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("$enumlength").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.replay(invocation);

+        invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setValue("High");

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        Result filterResult = compatibleFilter.invoke(invoker, invocation);

+        assertEquals(filterResult, result);

+    }

+

+    @Test

+    public void testResulthasException() {

+        invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("enumlength").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.replay(invocation);

+        invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setException(new RuntimeException());

+        result.setValue("High");

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        Result filterResult = compatibleFilter.invoke(invoker, invocation);

+        assertEquals(filterResult, result);

+    }

+

+    @Test

+    public void testInvokerJsonPojoSerialization() {

+        invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("enumlength").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Type[].class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.replay(invocation);

+        invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setValue("High");

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&serialization=json");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        Result filterResult = compatibleFilter.invoke(invoker, invocation);

+        assertEquals(Type.High, filterResult.getValue());

+    }

+

+    @Test

+    public void testInvokerNonJsonEnumSerialization() {

+        invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("enumlength").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Type[].class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.replay(invocation);

+        invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setValue("High");

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        Result filterResult = compatibleFilter.invoke(invoker, invocation);

+        assertEquals(Type.High, filterResult.getValue());

+    }

+    

+    @Test

+    public void testInvokerNonJsonNonPojoSerialization() {

+        invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] {String.class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.replay(invocation);

+        invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setValue(new String[]{"High"});

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        Result filterResult = compatibleFilter.invoke(invoker, invocation);

+        assertArrayEquals(new String[]{"High"}, (String[])filterResult.getValue());

+    }

+

+    @Test

+    public void testInvokerNonJsonPojoSerialization() {

+        invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { String.class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.replay(invocation);

+        invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setValue("hello");

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        Result filterResult = compatibleFilter.invoke(invoker, invocation);

+        assertEquals("hello", filterResult.getValue());

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilterTest.java
new file mode 100644
index 0000000..136a673
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilterTest.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import static org.junit.Assert.assertEquals;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.NetUtils;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.support.DemoService;

+import com.alibaba.dubbo.rpc.support.MockInvocation;

+import com.alibaba.dubbo.rpc.support.MyInvoker;

+

+/**

+ * ConsumerContextFilterTest.java

+ * @author tony.chenl

+ */

+public class ConsumerContextFilterTest {

+    Filter     consumerContextFilter = new ConsumerContextFilter();

+    @Test

+    public void testSetContext(){

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        Invoker<DemoService> invoker = new MyInvoker<DemoService>(url);

+        Invocation invocation = new MockInvocation();

+        consumerContextFilter.invoke(invoker, invocation);

+        assertEquals(invoker,RpcContext.getContext().getInvoker());

+        assertEquals(invocation,RpcContext.getContext().getInvocation());

+        assertEquals(NetUtils.getLocalHost() + ":0",RpcContext.getContext().getLocalAddressString());

+        assertEquals("test:11",RpcContext.getContext().getRemoteAddressString());

+        

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ContextFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ContextFilterTest.java
new file mode 100644
index 0000000..3a80175
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ContextFilterTest.java
@@ -0,0 +1,75 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import static org.junit.Assert.assertNull;

+

+import org.easymock.EasyMock;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcContext;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.support.DemoService;

+import com.alibaba.dubbo.rpc.support.MockInvocation;

+import com.alibaba.dubbo.rpc.support.MyInvoker;

+

+/**

+ * ContextFilterTest.java

+ * TODO 增强断言

+ * @author tony.chenl

+ */

+public class ContextFilterTest {

+

+    Filter               contextFilter = new ContextFilter();

+    Invoker<DemoService> invoker;

+    Invocation           invocation;

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testSetContext() {

+        invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("$enumlength").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();

+        EasyMock.replay(invocation);

+        invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setValue("High");

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        contextFilter.invoke(invoker, invocation);

+        assertNull(RpcContext.getContext().getInvoker());

+    }

+

+    @Test

+    public void testWithAttachments() {

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        Invoker<DemoService> invoker = new MyInvoker<DemoService>(url);

+        Invocation invocation = new MockInvocation();

+        Result result = contextFilter.invoke(invoker, invocation);

+        assertNull(RpcContext.getContext().getInvoker());

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilterTest.java
new file mode 100644
index 0000000..1d36041
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilterTest.java
@@ -0,0 +1,48 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import static org.junit.Assert.*;

+

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.utils.LogUtil;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.support.DemoService;

+import com.alibaba.dubbo.rpc.support.MockInvocation;

+import com.alibaba.dubbo.rpc.support.MyInvoker;

+

+/**

+ * DeprecatedFilterTest.java

+ * 

+ * @author tony.chenl

+ */

+public class DeprecatedFilterTest {

+

+    Filter deprecatedFilter = new DeprecatedFilter();

+

+    @Test

+    public void testDeprecatedFilter() {

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&echo." + Constants.DEPRECATED_KEY + "=true");

+        LogUtil.start();

+        deprecatedFilter.invoke(new MyInvoker<DemoService>(url), new MockInvocation());

+        assertEquals(1,

+                     LogUtil.findMessage("The service method com.alibaba.dubbo.rpc.support.DemoService.echo(String) is DEPRECATED"));

+        LogUtil.stop();

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/EchoFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/EchoFilterTest.java
new file mode 100644
index 0000000..51c8a5a
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/EchoFilterTest.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import static org.junit.Assert.assertEquals;

+

+import org.easymock.EasyMock;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcResult;

+import com.alibaba.dubbo.rpc.support.DemoService;

+

+/**

+ * EchoFilterTest.java

+ * 

+ * @author tony.chenl

+ */

+public class EchoFilterTest {

+

+    Filter echoFilter = new EchoFilter();

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testEcho() {

+        Invocation invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("$echo").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();

+        EasyMock.replay(invocation);

+        Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setValue("High");

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        Result filterResult = echoFilter.invoke(invoker, invocation);

+        assertEquals("hello", filterResult.getValue());

+    }

+

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testNonEcho() {

+        Invocation invocation = EasyMock.createMock(Invocation.class);

+        EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();

+        EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();

+        EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();

+        EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();

+        EasyMock.replay(invocation);

+        Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();

+        RpcResult result = new RpcResult();

+        result.setValue("High");

+        EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();

+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

+        EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();

+        EasyMock.replay(invoker);

+        Result filterResult = echoFilter.invoke(invoker, invocation);

+        assertEquals("High", filterResult.getValue());

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ExceptionFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ExceptionFilterTest.java
new file mode 100644
index 0000000..8645d53
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ExceptionFilterTest.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.filter;

+

+import static org.junit.Assert.assertEquals;

+

+import org.easymock.EasyMock;

+import org.junit.Test;

+

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcInvocation;

+import com.alibaba.dubbo.rpc.support.DemoService;

+

+/**

+ * ExceptionFilterTest

+ * 

+ * @author william.liangf

+ */

+public class ExceptionFilterTest {

+    

+    @SuppressWarnings("unchecked")

+    @Test

+    public void testRpcException() {

+        Logger logger = EasyMock.createMock(Logger.class);

+        RpcException exception = new RpcException("TestRpcException");

+        logger.error(EasyMock.eq("Got unchecked and undeclare service " + DemoService.class.getName() + " method sayHello invoke exception: TestRpcException"), EasyMock.eq(exception));

+        ExceptionFilter exceptionFilter = new ExceptionFilter(logger);

+        RpcInvocation invocation = new RpcInvocation("sayHello", new Class<?>[]{String.class}, new Object[]{"world"});

+        Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);

+        EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class);

+        EasyMock.expect(invoker.invoke(EasyMock.eq(invocation))).andThrow(exception);

+        

+        EasyMock.replay(logger, invoker);

+        

+        try {

+            exceptionFilter.invoke(invoker, invocation);

+        } catch (RpcException e) {

+            assertEquals("TestRpcException", e.getMessage());

+        }

+        EasyMock.verify(logger, invoker);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilterTest.java
new file mode 100644
index 0000000..99d8e61
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilterTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *    
+ *   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.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcStatus;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MyInvoker;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class TpsLimitFilterTest {
+
+    private TpsLimitFilter filter = new TpsLimitFilter();
+
+    @Test
+    public void testWithoutCount() throws Exception {
+        URL url = URL.valueOf("test://test");
+        url = url.addParameter(Constants.TPS_MAX_KEY, 5);
+        Invoker<TpsLimitFilterTest> invoker = new MyInvoker<TpsLimitFilterTest>(url);
+        Invocation invocation = new MockInvocation();
+        filter.invoke(invoker, invocation);
+    }
+    
+    @Test(expected = RpcException.class)
+    public void testFail() throws Exception {
+        URL url = URL.valueOf("test://test");
+        url = url.addParameter(Constants.TPS_MAX_KEY, 5);
+        Invoker<TpsLimitFilterTest> invoker = new MyInvoker<TpsLimitFilterTest>(url);
+        Invocation invocation = new MockInvocation();
+        for (int i = 0; i < 10; i++) {
+            RpcStatus.beginCount(url, invocation.getMethodName());
+            Thread.sleep(100);
+            RpcStatus.endCount(url, invocation.getMethodName(), 100, true);
+        }
+        filter.invoke(invoker, invocation);
+    }
+
+}
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoRequest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoRequest.java
new file mode 100644
index 0000000..9a2da25
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoRequest.java
@@ -0,0 +1,64 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy;
+
+import java.io.Serializable;
+
+/**
+ * TestRequest.
+ * 
+ * @author qian.lei
+ */
+
+class DemoRequest implements Serializable
+{
+	private static final long serialVersionUID = -2579095288792344869L;
+
+	private String mServiceName;
+	
+	private String mMethodName;
+
+	private Class<?>[] mParameterTypes;
+
+	private Object[] mArguments;
+
+	public DemoRequest(String serviceName,String methodName, Class<?>[] parameterTypes,Object[] args)
+	{
+		mServiceName = serviceName;
+		mMethodName = methodName;
+		mParameterTypes = parameterTypes;
+		mArguments = args;
+	}
+
+	public String getServiceName()
+	{
+		return mServiceName;
+	}
+
+	public String getMethodName()
+	{
+		return mMethodName;
+	}
+
+	public Class<?>[] getParameterTypes() {
+		return mParameterTypes;
+	}
+
+	public Object[] getArguments()
+	{
+		return mArguments;
+	}
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoService.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoService.java
new file mode 100644
index 0000000..917262a
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoService.java
@@ -0,0 +1,41 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+	void sayHello(String name);
+
+	String echo(String text);
+
+	long timestamp();
+
+	String getThreadName();
+
+	int getSize(String[] strs);
+
+	int getSize(Object[] os);
+
+	Object invoke(String service, String method) throws Exception;
+
+	int stringLength(String str);
+
+	Type enumlength(Type... types);
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoServiceImpl.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoServiceImpl.java
new file mode 100644
index 0000000..de02c54
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoServiceImpl.java
@@ -0,0 +1,80 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+/**
+ * DemoServiceImpl
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+	public DemoServiceImpl()
+	{
+		super();
+	}
+
+	public void sayHello(String name) {
+		System.out.println("hello "+name);
+	}
+
+	public String echo(String text)
+	{
+		return text;
+	}
+
+	public long timestamp() {
+		return System.currentTimeMillis();
+	}
+
+	public String getThreadName()
+	{
+		return Thread.currentThread().getName();
+	}
+
+	public int getSize(String[] strs)
+	{
+		if( strs == null )
+			return -1;
+		return strs.length;
+	}
+
+	public int getSize(Object[] os)
+	{
+		if( os == null )
+			return -1;
+		return os.length;
+	}
+
+	public Object invoke(String service, String method) throws Exception
+	{
+		System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+		return service + ":" + method;
+	}
+
+	public Type enumlength(Type... types)
+	{
+		if( types.length == 0 )
+			return Type.Lower;
+		return types[0];
+	}
+
+	public int stringLength(String str)
+	{
+		return str.length();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteService.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteService.java
new file mode 100644
index 0000000..194b0c7
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteService.java
@@ -0,0 +1,26 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteService extends Remote
+{
+	String sayHello(String name) throws RemoteException;
+
+	String getThreadName() throws RemoteException;
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteServiceImpl.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteServiceImpl.java
new file mode 100644
index 0000000..5426978
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteServiceImpl.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy;
+
+import java.rmi.RemoteException;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+public class RemoteServiceImpl implements RemoteService
+{
+	public String getThreadName() throws RemoteException
+	{
+		System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+		return Thread.currentThread().getName();
+	}
+
+	public String sayHello(String name) throws RemoteException
+	{
+		return "hello " + name + "@" + RemoteServiceImpl.class.getName();
+	}
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/Type.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/Type.java
new file mode 100644
index 0000000..ead26d1
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/Type.java
@@ -0,0 +1,21 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.proxy;
+
+public enum Type
+{
+	High, Normal, Lower
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoService.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoService.java
new file mode 100644
index 0000000..f191ae8
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoService.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;
+

+import com.alibaba.dubbo.rpc.CustomArgument;

+
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+	void sayHello(String name);
+
+	String echo(String text);
+
+	long timestamp();
+
+	String getThreadName();
+
+	int getSize(String[] strs);
+
+	int getSize(Object[] os);
+
+	Object invoke(String service, String method) throws Exception;
+
+	int stringLength(String str);
+
+	Type enumlength(Type... types);
+	
+//	Type enumlength(Type type);
+	
+	String get(CustomArgument arg1);
+	
+	byte getbyte(byte arg);

+	
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoServiceImpl.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoServiceImpl.java
new file mode 100644
index 0000000..8b602ee
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoServiceImpl.java
@@ -0,0 +1,97 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;
+

+import com.alibaba.dubbo.rpc.CustomArgument;

+import com.alibaba.dubbo.rpc.RpcContext;

+
+/**
+ * DemoServiceImpl
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+	public DemoServiceImpl()
+	{
+		super();
+	}
+
+	public void sayHello(String name) {
+		System.out.println("hello "+name);
+	}
+
+	public String echo(String text)
+	{
+		return text;
+	}
+
+	public long timestamp() {
+		return System.currentTimeMillis();
+	}
+
+	public String getThreadName()
+	{
+		return Thread.currentThread().getName();
+	}
+
+	public int getSize(String[] strs)
+	{
+		if( strs == null )
+			return -1;
+		return strs.length;
+	}
+
+	public int getSize(Object[] os)
+	{
+		if( os == null )
+			return -1;
+		return os.length;
+	}
+
+	public Object invoke(String service, String method) throws Exception
+	{
+		System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+		return service + ":" + method;
+	}
+
+	public Type enumlength(Type... types)
+	{
+		if( types.length == 0 )
+			return Type.Lower;
+		return types[0];
+	}
+	
+	public Type enumlength(Type type)
+    {
+       return type;
+    }
+
+	public int stringLength(String str)
+	{
+		return str.length();
+	}
+	public String get(CustomArgument arg1){
+	    return arg1.toString();
+	}
+
+    public byte getbyte(byte arg) {
+        return arg;
+    }

+

+    public Person gerPerson(Person person) {

+        return person;

+    }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/IEcho.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/IEcho.java
new file mode 100644
index 0000000..4a63a5c
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/IEcho.java
@@ -0,0 +1,20 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;
+
+public interface IEcho {
+    String echo(String e);
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.java
new file mode 100644
index 0000000..9910e82
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.java
@@ -0,0 +1,67 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;

+

+import java.util.HashMap;

+import java.util.Map;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+

+/**

+ * MockInvocation.java

+ * 

+ * @author tony.chenl

+ */

+public class MockInvocation implements Invocation {

+

+    public String getMethodName() {

+        return "echo";

+    }

+

+    public Class<?>[] getParameterTypes() {

+        return new Class[] { String.class };

+    }

+

+    public Object[] getArguments() {

+        return new Object[] { "aa" };

+    }

+

+    public Map<String, String> getAttachments() {

+        Map<String, String> attachments = new HashMap<String, String>();

+        attachments.put(Constants.PATH_KEY, "dubbo");

+        attachments.put(Constants.GROUP_KEY, "dubbo");

+        attachments.put(Constants.VERSION_KEY, "1.0.0");

+        attachments.put(Constants.DUBBO_VERSION_KEY, "1.0.0");

+        attachments.put(Constants.TOKEN_KEY, "sfag");

+        attachments.put(Constants.TIMEOUT_KEY, "1000");

+        return attachments;

+    }

+

+    public Invoker<?> getInvoker() {

+        return null;

+    }

+

+    public String getAttachment(String key) {

+        return getAttachments().get(key);

+    }

+

+    public String getAttachment(String key, String defaultValue) {

+        return getAttachments().get(key);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MyInvoker.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MyInvoker.java
new file mode 100644
index 0000000..2c2b6f8
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MyInvoker.java
@@ -0,0 +1,74 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.rpc.RpcResult;

+

+/**

+ * MockInvoker.java

+ * 

+ * @author tony.chenl

+ */

+public class MyInvoker<T> implements Invoker<T> {

+

+    URL      url;

+    Class<T> type;

+    boolean  hasException = false;

+

+    public MyInvoker(URL url){

+        this.url = url;

+        type = (Class<T>) DemoService.class;

+    }

+

+    public MyInvoker(URL url, boolean hasException){

+        this.url = url;

+        type = (Class<T>) DemoService.class;

+        this.hasException = hasException;

+    }

+

+    public Class<T> getInterface() {

+        return type;

+    }

+

+    public URL getUrl() {

+        return url;

+    }

+

+    public boolean isAvailable() {

+        return false;

+    }

+

+    public Result invoke(Invocation invocation) throws RpcException {

+        RpcResult result = new RpcResult();

+        if (hasException == false) {

+            result.setValue("alibaba");

+            return result;

+        } else {

+            result.setException(new RuntimeException("mocked exception"));

+            return result;

+        }

+

+    }

+

+    public void destroy() {

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Person.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Person.java
new file mode 100644
index 0000000..19db863
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Person.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;

+

+import java.io.Serializable;

+

+/**

+ * Person.java

+ * 

+ * @author tony.chenl

+ */

+public class Person implements Serializable {

+

+    private static final long serialVersionUID = 1L;

+    private String            name;

+    private int               age;

+

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        this.name = name;

+    }

+

+    public int getAge() {

+        return age;

+    }

+

+    public void setAge(int age) {

+        this.age = age;

+    }

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Type.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Type.java
new file mode 100644
index 0000000..ed1dd9a
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Type.java
@@ -0,0 +1,21 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * 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.dubbo.rpc.support;
+
+public enum Type
+{
+	High, Normal, Lower
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/resources/log4j.xml b/dubbo-rpc/src/test/resources/log4j.xml
new file mode 100644
index 0000000..e6c81db
--- /dev/null
+++ b/dubbo-rpc/src/test/resources/log4j.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+	<!-- ===================================================================== -->
+	<!-- 以下是appender的定义 -->
+	<!-- ===================================================================== -->
+	<appender name="dubbo" class="com.alibaba.dubbo.common.utils.DubboAppender">
+		<param name="encoding" value="GBK" />
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="%d %p [%c:%M] - %m%n" />
+		</layout>
+		<!-- <filter class="org.apache.log4j.varia.LevelRangeFilter">
+			<param name="LevelMin" value="DEBUG" />
+			<param name="LevelMax" value="DEBUG" />
+		</filter> -->
+	</appender>
+	<root>
+		<level value="INFO" />
+		<appender-ref ref="dubbo" />
+	</root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-validation/pom.xml b/dubbo-validation/pom.xml
new file mode 100644
index 0000000..176a1d5
--- /dev/null
+++ b/dubbo-validation/pom.xml
@@ -0,0 +1,43 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+	<modelVersion>4.0.0</modelVersion>

+	<parent>

+		<groupId>com.alibaba</groupId>

+		<artifactId>dubbo-parent</artifactId>

+		<version>2.1.1</version>

+	</parent>

+	<artifactId>dubbo-validation</artifactId>

+	<packaging>jar</packaging>

+	<name>Dubbo Validation Module</name>

+	<description>The validation module of dubbo project</description>

+	<properties>

+		<skip_maven_deploy>true</skip_maven_deploy>

+	</properties>

+	<dependencies>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>javax.validation</groupId>

+			<artifactId>validation-api</artifactId>

+			<scope>provided</scope>

+		</dependency>

+	</dependencies>

+</project>
\ No newline at end of file
diff --git a/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/Validation.java b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/Validation.java
new file mode 100644
index 0000000..107fd2c
--- /dev/null
+++ b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/Validation.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.validation;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.extension.Adaptive;

+import com.alibaba.dubbo.common.extension.SPI;

+

+/**

+ * Validation

+ * 

+ * @author william.liangf

+ */

+@SPI("jvalidation")

+public interface Validation {

+

+    @Adaptive(Constants.VALIDATION_KEY)

+    Validator getValidator(URL url);

+

+}

diff --git a/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/Validator.java b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/Validator.java
new file mode 100644
index 0000000..3e0b62e
--- /dev/null
+++ b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/Validator.java
@@ -0,0 +1,29 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.validation;

+

+import com.alibaba.dubbo.rpc.Invocation;

+

+/**

+ * Validator

+ * 

+ * @author william.liangf

+ */

+public interface Validator {

+

+    void validate(Invocation invocation) throws Exception;

+

+}

diff --git a/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/filter/ValidationFilter.java b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/filter/ValidationFilter.java
new file mode 100644
index 0000000..cb8773f
--- /dev/null
+++ b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/filter/ValidationFilter.java
@@ -0,0 +1,59 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.validation.filter;

+

+import com.alibaba.dubbo.common.Constants;

+import com.alibaba.dubbo.common.extension.Activate;

+import com.alibaba.dubbo.common.utils.ConfigUtils;

+import com.alibaba.dubbo.rpc.Filter;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.rpc.Invoker;

+import com.alibaba.dubbo.rpc.Result;

+import com.alibaba.dubbo.rpc.RpcException;

+import com.alibaba.dubbo.validation.Validation;

+import com.alibaba.dubbo.validation.Validator;

+

+/**

+ * ValidationFilter

+ * 

+ * @author william.liangf

+ */

+@Activate(group = { Constants.CONSUMER, Constants.PROVIDER }, value = Constants.VALIDATION_KEY)

+public class ValidationFilter implements Filter {

+

+    private Validation validation;

+

+    public void setValidation(Validation validation) {

+        this.validation = validation;

+    }

+

+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

+        if (validation != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.VALIDATION_KEY))) {

+            Validator validator = validation.getValidator(invoker.getUrl());

+            if (validator != null) {

+                try {

+                    validator.validate(invocation);

+                } catch (RpcException e) {

+                    throw e;

+                } catch (Throwable t) {

+                    throw new RpcException(t.getMessage(), t);

+                }

+            }

+        }

+        return invoker.invoke(invocation);

+    }

+

+}

diff --git a/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/support/AbstractValidation.java b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/support/AbstractValidation.java
new file mode 100644
index 0000000..9a866c9
--- /dev/null
+++ b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/support/AbstractValidation.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.validation.support;

+

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.ConcurrentMap;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.validation.Validation;

+import com.alibaba.dubbo.validation.Validator;

+

+/**

+ * AbstractValidation

+ * 

+ * @author william.liangf

+ */

+public abstract class AbstractValidation implements Validation {

+

+    private final ConcurrentMap<String, Validator> validators = new ConcurrentHashMap<String, Validator>();

+

+    public Validator getValidator(URL url) {

+        String key = url.toFullString();

+        Validator validator = validators.get(key);

+        if (validator == null) {

+            validators.put(key, createValidator(url));

+            validator = validators.get(key);

+        }

+        return validator;

+    }

+

+    protected abstract Validator createValidator(URL url);

+

+}

diff --git a/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidation.java b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidation.java
new file mode 100644
index 0000000..06ec0a0
--- /dev/null
+++ b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidation.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.validation.support.jvalidation;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.validation.Validator;

+import com.alibaba.dubbo.validation.support.AbstractValidation;

+

+/**

+ * JValidation

+ * 

+ * @author william.liangf

+ */

+public class JValidation extends AbstractValidation {

+

+    @Override

+    protected Validator createValidator(URL url) {

+        return new JValidator(url);

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidator.java b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidator.java
new file mode 100644
index 0000000..25e3b41
--- /dev/null
+++ b/dubbo-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidator.java
@@ -0,0 +1,280 @@
+/*

+ * Copyright 1999-2012 Alibaba Group.

+ *  

+ * 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.dubbo.validation.support.jvalidation;

+

+import java.lang.annotation.Annotation;

+import java.lang.reflect.Array;

+import java.lang.reflect.Field;

+import java.lang.reflect.Method;

+import java.util.Collection;

+import java.util.Date;

+import java.util.HashSet;

+import java.util.Map;

+import java.util.Set;

+

+import javassist.ClassPool;

+import javassist.CtClass;

+import javassist.CtField;

+import javassist.CtNewConstructor;

+import javassist.Modifier;

+import javassist.NotFoundException;

+import javassist.bytecode.AnnotationsAttribute;

+import javassist.bytecode.ClassFile;

+import javassist.bytecode.ConstPool;

+import javassist.bytecode.annotation.AnnotationMemberValue;

+import javassist.bytecode.annotation.ArrayMemberValue;

+import javassist.bytecode.annotation.BooleanMemberValue;

+import javassist.bytecode.annotation.ByteMemberValue;

+import javassist.bytecode.annotation.CharMemberValue;

+import javassist.bytecode.annotation.ClassMemberValue;

+import javassist.bytecode.annotation.DoubleMemberValue;

+import javassist.bytecode.annotation.EnumMemberValue;

+import javassist.bytecode.annotation.FloatMemberValue;

+import javassist.bytecode.annotation.IntegerMemberValue;

+import javassist.bytecode.annotation.LongMemberValue;

+import javassist.bytecode.annotation.MemberValue;

+import javassist.bytecode.annotation.ShortMemberValue;

+import javassist.bytecode.annotation.StringMemberValue;

+

+import javax.validation.Constraint;

+import javax.validation.ConstraintViolation;

+import javax.validation.ConstraintViolationException;

+import javax.validation.Validation;

+import javax.validation.ValidatorFactory;

+import javax.validation.groups.Default;

+

+import com.alibaba.dubbo.common.URL;

+import com.alibaba.dubbo.common.bytecode.ClassGenerator;

+import com.alibaba.dubbo.common.logger.Logger;

+import com.alibaba.dubbo.common.logger.LoggerFactory;

+import com.alibaba.dubbo.common.utils.ReflectUtils;

+import com.alibaba.dubbo.rpc.Invocation;

+import com.alibaba.dubbo.validation.Validator;

+

+/**

+ * JValidator

+ * 

+ * @author william.liangf

+ */

+public class JValidator implements Validator {

+

+    private static final Logger logger = LoggerFactory.getLogger(JValidator.class);

+

+    private final Class<?> clazz;

+    

+    private final javax.validation.Validator validator;

+

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    public JValidator(URL url) {

+        this.clazz = ReflectUtils.forName(url.getServiceInterface());

+        String jvalidation = url.getParameter("jvalidation");

+        ValidatorFactory factory;

+        if (jvalidation != null && jvalidation.length() > 0) {

+            factory = Validation.byProvider((Class)ReflectUtils.forName(jvalidation)).configure().buildValidatorFactory();

+        } else {

+            factory = Validation.buildDefaultValidatorFactory();

+        }

+        this.validator = factory.getValidator();

+    }

+

+    public void validate(Invocation invocation) throws Exception {

+        String methodName = invocation.getMethodName();

+        String methodClassName = clazz.getName() + "$" + toUpperMethoName(methodName);

+        Class<?> methodClass = null;

+        try {

+            methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader());

+        } catch (ClassNotFoundException e) {

+        }

+        Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>>();

+        Method method = clazz.getMethod(methodName, invocation.getParameterTypes());

+        Object parameterBean = getMethodParameterBean(clazz, method, invocation.getArguments());

+        if (parameterBean != null) {

+            if (methodClass != null) {

+                violations.addAll(validator.validate(parameterBean, Default.class, clazz, methodClass));

+            } else {

+                violations.addAll(validator.validate(parameterBean, Default.class, clazz));

+            }

+        }

+        for (Object arg : invocation.getArguments()) {

+            validate(violations, arg, clazz, methodClass);

+        }

+        if (violations.size() > 0) {

+            throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations);

+        }

+    }

+

+    private void validate(Set<ConstraintViolation<?>> violations, Object arg, Class<?> clazz, Class<?> methodClass) {

+        if (arg != null && ! isPrimitives(arg.getClass())) {

+            if (Object[].class.isInstance(arg)) {

+                for (Object item : (Object[]) arg) {

+                    validate(violations, item, clazz, methodClass);

+                }

+            } else if (Collection.class.isInstance(arg)) {

+                for (Object item : (Collection<?>) arg) {

+                    validate(violations, item, clazz, methodClass);

+                }

+            } else if (Map.class.isInstance(arg)) {

+                for (Map.Entry<?, ?> entry : ((Map<?, ?>) arg).entrySet()) {

+                    validate(violations, entry.getKey(), clazz, methodClass);

+                    validate(violations, entry.getValue(), clazz, methodClass);

+                }

+            } else {

+                if (methodClass != null) {

+                    violations.addAll(validator.validate(arg, Default.class, clazz, methodClass));

+                } else {

+                    violations.addAll(validator.validate(arg, Default.class, clazz));

+                }

+            }

+        }

+    }

+    

+    private static boolean isPrimitives(Class<?> cls) {

+        if (cls.isArray()) {

+            return isPrimitive(cls.getComponentType());

+        }

+        return isPrimitive(cls);

+    }

+    

+    private static boolean isPrimitive(Class<?> cls) {

+        return cls.isPrimitive() || cls == String.class || cls == Boolean.class || cls == Character.class 

+                || Number.class.isAssignableFrom(cls) || Date.class.isAssignableFrom(cls);

+    }

+    

+    private static Object getMethodParameterBean(Class<?> clazz, Method method, Object[] args) {

+        if (! hasConstraintParameter(method)) {

+            return null;

+        }

+        try {

+            String upperName = toUpperMethoName(method.getName());

+            String parameterSimpleName = upperName + "Parameter";

+            String parameterClassName = clazz.getName() + "$" + parameterSimpleName;

+            Class<?> parameterClass;

+            try {

+                parameterClass = (Class<?>) Class.forName(parameterClassName, true, clazz.getClassLoader());

+            } catch (ClassNotFoundException e) {

+                ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());

+                CtClass ctClass = pool.makeClass(parameterClassName);

+                ClassFile classFile = ctClass.getClassFile();

+                classFile.setVersionToJava5();

+                ctClass.addConstructor(CtNewConstructor.defaultConstructor(pool.getCtClass(parameterClassName)));

+                // parameter fields

+                Class<?>[] parameterTypes = method.getParameterTypes();

+                Annotation[][] parameterAnnotations = method.getParameterAnnotations();

+                for (int i = 0; i < parameterTypes.length; i ++) {

+                    Class<?> type = parameterTypes[i];

+                    Annotation[] annotations = parameterAnnotations[i];

+                    AnnotationsAttribute attribute = new AnnotationsAttribute(classFile.getConstPool(), AnnotationsAttribute.visibleTag);

+                    for (Annotation annotation : annotations) {

+                        if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {

+                            javassist.bytecode.annotation.Annotation ja = new javassist.bytecode.annotation.Annotation(

+                                    classFile.getConstPool(), pool.get(annotation.annotationType().getName()));

+                            Method[] members = annotation.annotationType().getMethods();

+                            for (Method member : members) {

+                                if (Modifier.isPublic(member.getModifiers())

+                                        && member.getParameterTypes().length == 0

+                                        && member.getDeclaringClass() == annotation.annotationType()) {

+                                    Object value = member.invoke(annotation, new Object[0]);

+                                    MemberValue memberValue = createMemberValue(

+                                            classFile.getConstPool(), pool.getCtClass(member.getReturnType().getCanonicalName()), value);

+                                    ja.addMemberValue(member.getName(), memberValue);

+                                }

+                            }

+                            attribute.addAnnotation(ja);

+                        }

+                    }

+                    String fieldName = method.getName() + "Argument" + i;

+                    CtField ctField = CtField.make("public " + type.getCanonicalName() + " " + fieldName + ";", pool.getCtClass(parameterClassName));

+                    ctField.getFieldInfo().addAttribute(attribute);

+                    ctClass.addField(ctField);

+                }

+                parameterClass = ctClass.toClass();

+            }

+            Object parameterBean = parameterClass.newInstance();

+            for (int i = 0; i < args.length; i ++) {

+                Field field = parameterClass.getField(method.getName() + "Argument" + i);

+                field.set(parameterBean, args[0]);

+            }

+            return parameterBean;

+        } catch (Throwable e) {

+            logger.warn(e.getMessage(), e);

+            return null;

+        }

+    }

+

+    private static boolean hasConstraintParameter(Method method) {

+        Annotation[][] parameterAnnotations = method.getParameterAnnotations();

+        if (parameterAnnotations != null && parameterAnnotations.length > 0) {

+            for (Annotation[] annotations : parameterAnnotations) {

+                for (Annotation annotation : annotations) {

+                    if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {

+                        return true;

+                    }

+                }

+            }

+        }

+        return false;

+    }

+

+    private static String toUpperMethoName(String methodName) {

+        return methodName.substring(0, 1).toUpperCase() + methodName.substring(1);

+    }

+    

+    // Copy from javassist.bytecode.annotation.Annotation.createMemberValue(ConstPool, CtClass);

+    private static MemberValue createMemberValue(ConstPool cp, CtClass type, Object value) throws NotFoundException {

+        if (type == CtClass.booleanType)

+            return new BooleanMemberValue((Boolean) value, cp);

+        else if (type == CtClass.byteType)

+            return new ByteMemberValue((Byte) value, cp);

+        else if (type == CtClass.charType)

+            return new CharMemberValue((Character) value, cp);

+        else if (type == CtClass.shortType)

+            return new ShortMemberValue((Short) value, cp);

+        else if (type == CtClass.intType)

+            return new IntegerMemberValue((Integer) value, cp);

+        else if (type == CtClass.longType)

+            return new LongMemberValue((Long) value, cp);

+        else if (type == CtClass.floatType)

+            return new FloatMemberValue((Float) value, cp);

+        else if (type == CtClass.doubleType)

+            return new DoubleMemberValue((Double) value, cp);

+        else if (type.getName().equals("java.lang.Class"))

+            return new ClassMemberValue(((Class<?>) value).getName(), cp);

+        else if (type.getName().equals("java.lang.String"))

+            return new StringMemberValue((String) value, cp);

+        else if (type.isArray()) {

+            CtClass arrayType = type.getComponentType();

+            int len = Array.getLength(value);

+            MemberValue[] members = new MemberValue[len];

+            for (int i = 0; i < len; i ++) {

+                members[i] = createMemberValue(cp, arrayType, Array.get(value, i));

+            }

+            ArrayMemberValue arrayMemberValue = new ArrayMemberValue(cp);

+            arrayMemberValue.setValue(members);

+            return arrayMemberValue;

+        } else if (type.isInterface()) {

+            javassist.bytecode.annotation.Annotation info = new javassist.bytecode.annotation.Annotation(cp, type);

+            return new AnnotationMemberValue(info, cp);

+        } else {

+            // treat as enum.  I know this is not typed,

+            // but JBoss has an Annotation Compiler for JDK 1.4

+            // and I want it to work with that. - Bill Burke

+            EnumMemberValue emv = new EnumMemberValue(cp);

+            emv.setType(type.getName());

+            return emv;

+        }

+    }

+

+}

diff --git a/dubbo-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..2e3e5b3
--- /dev/null
+++ b/dubbo-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+validation=com.alibaba.dubbo.validation.filter.ValidationFilter
\ No newline at end of file
diff --git a/dubbo-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.validation.Validation b/dubbo-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.validation.Validation
new file mode 100644
index 0000000..7f344d4
--- /dev/null
+++ b/dubbo-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.validation.Validation
@@ -0,0 +1 @@
+jvalidation=com.alibaba.dubbo.validation.support.jvalidation.JValidation
\ No newline at end of file
diff --git a/dubbo/pom.xml b/dubbo/pom.xml
new file mode 100644
index 0000000..19296b2
--- /dev/null
+++ b/dubbo/pom.xml
@@ -0,0 +1,349 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - 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.

+-->

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+	<modelVersion>4.0.0</modelVersion>

+	<parent>

+		<groupId>com.alibaba</groupId>

+		<artifactId>dubbo-parent</artifactId>

+		<version>2.1.1</version>

+	</parent>

+	<artifactId>dubbo</artifactId>

+	<packaging>jar</packaging>

+	<name>Dubbo All In One</name>

+	<description>The all in one project of dubbo</description>

+	<properties>

+		<skip_maven_deploy>false</skip_maven_deploy>

+	</properties>

+	<dependencies>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-config</artifactId>

+			<version>${project.parent.version}</version>

+			<exclusions>

+				<exclusion>

+					<groupId>org.mortbay.jetty</groupId>

+					<artifactId>jetty</artifactId>

+				</exclusion>

+			</exclusions>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-netty</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-mina</artifactId>

+			<version>${project.parent.version}</version>

+			<exclusions>

+				<exclusion>

+					<groupId>org.apache.mina</groupId>

+					<artifactId>mina-core</artifactId>

+				</exclusion>

+				<exclusion>

+					<groupId>org.slf4j</groupId>

+					<artifactId>slf4j-api</artifactId>

+				</exclusion>

+			</exclusions>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-grizzly</artifactId>

+			<version>${project.parent.version}</version>

+			<exclusions>

+				<exclusion>

+					<groupId>org.glassfish.grizzly</groupId>

+					<artifactId>grizzly-core</artifactId>

+				</exclusion>

+			</exclusions>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-p2p</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-remoting-http</artifactId>

+			<version>${project.parent.version}</version>

+			<exclusions>

+				<exclusion>

+					<groupId>org.mortbay.jetty</groupId>

+					<artifactId>jetty</artifactId>

+				</exclusion>

+				<exclusion>

+					<groupId>org.apache.httpcomponents</groupId>

+		    		<artifactId>httpclient</artifactId>

+				</exclusion>

+			</exclusions>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-default</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-injvm</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-rmi</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-rpc-hessian</artifactId>

+			<version>${project.parent.version}</version>

+			<exclusions>

+				<exclusion>

+					<groupId>com.caucho</groupId>

+    				<artifactId>hessian</artifactId>

+				</exclusion>

+			</exclusions>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-registry-default</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-registry-multicast</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-registry-zookeeper</artifactId>

+			<version>${project.parent.version}</version>

+			<exclusions>

+				<exclusion>

+					<groupId>org.apache.zookeeper</groupId>

+		    		<artifactId>zookeeper</artifactId>

+				</exclusion>

+			</exclusions>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-registry-redis</artifactId>

+			<version>${project.parent.version}</version>

+			<exclusions>

+				<exclusion>

+					<groupId>redis.clients</groupId>

+					<artifactId>jedis</artifactId>

+				</exclusion>

+			</exclusions>

+		</dependency>

+		<dependency>

+			<groupId>com.alibaba</groupId>

+			<artifactId>dubbo-monitor-default</artifactId>

+			<version>${project.parent.version}</version>

+		</dependency>

+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<artifactId>maven-source-plugin</artifactId>

+				<executions>

+					<execution>

+						<id>attach-sources</id>

+						<phase>none</phase>

+					</execution>

+				</executions>

+			</plugin>

+			<plugin>

+				<artifactId>maven-javadoc-plugin</artifactId>

+				<executions>

+                    <execution>

+                        <id>attach-javadoc</id>

+						<phase>deploy</phase>

+                        <goals>

+                            <goal>jar</goal>

+                        </goals>

+                    </execution>

+                </executions>

+				<configuration>

+					<show>public</show>

+					<charset>UTF-8</charset>

+					<encoding>UTF-8</encoding>

+					<docencoding>UTF-8</docencoding>

+					<excludePackageNames>com.alibaba.com.*</excludePackageNames>

+					<links>

+						<link>http://docs.oracle.com/javase/6/docs/api</link>

+					</links>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.maven.plugins</groupId>

+				<artifactId>maven-shade-plugin</artifactId>

+				<version>1.4</version>

+				<executions>

+					<execution>

+						<phase>package</phase>

+						<goals>

+							<goal>shade</goal>

+						</goals>

+						<configuration>

+							<createSourcesJar>true</createSourcesJar>

+							<promoteTransitiveDependencies>true</promoteTransitiveDependencies>

+							<artifactSet>

+								<includes>

+								    <include>com.alibaba:hessian-lite</include>

+									<include>com.alibaba:dubbo-common</include>

+									<include>com.alibaba:dubbo-remoting</include>

+									<include>com.alibaba:dubbo-remoting-netty</include>

+									<include>com.alibaba:dubbo-remoting-mina</include>

+									<include>com.alibaba:dubbo-remoting-grizzly</include>

+									<include>com.alibaba:dubbo-remoting-p2p</include>

+									<include>com.alibaba:dubbo-remoting-http</include>

+									<include>com.alibaba:dubbo-rpc</include>

+									<include>com.alibaba:dubbo-rpc-default</include>

+									<include>com.alibaba:dubbo-rpc-injvm</include>

+									<include>com.alibaba:dubbo-rpc-rmi</include>

+									<include>com.alibaba:dubbo-rpc-hessian</include>

+									<include>com.alibaba:dubbo-validation</include>

+									<include>com.alibaba:dubbo-cache</include>

+									<include>com.alibaba:dubbo-cluster</include>

+									<include>com.alibaba:dubbo-registry</include>

+									<include>com.alibaba:dubbo-registry-default</include>

+									<include>com.alibaba:dubbo-registry-multicast</include>

+									<include>com.alibaba:dubbo-registry-zookeeper</include>

+									<include>com.alibaba:dubbo-registry-redis</include>

+									<include>com.alibaba:dubbo-monitor</include>

+									<include>com.alibaba:dubbo-monitor-default</include>

+									<include>com.alibaba:dubbo-config</include>

+									<include>com.alibaba:dubbo-container</include>

+								</includes>

+							</artifactSet>

+							<transformers>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.container.Container</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.remoting.Dispather</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.remoting.Codec</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.remoting.http.HttpBinder</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.rpc.Filter</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory</resource>

+								</transformer>

+								<transformer

+									implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

+									<resource>META-INF/dubbo/com.alibaba.dubbo.validation.Validation</resource>

+								</transformer>

+							</transformers>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+		</plugins>

+	</build>

+</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..39c2c8b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,454 @@
+<!-- 
+ - Copyright 1999-2011 Alibaba Group.
+ -  
+ - 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.alibaba</groupId>
+		<artifactId>opensesame</artifactId>
+		<version>1.0</version>
+	</parent>
+	<artifactId>dubbo-parent</artifactId>
+	<version>2.1.1</version>
+	<packaging>pom</packaging>
+	<name>Dubbo Parent POM</name>
+	<description>The parent project of dubbo</description>
+	<url>http://code.alibabatech.com/wiki/display/dubbo</url>
+	<inceptionYear>2011</inceptionYear>
+	<licenses>
+		<license>
+			<name>Apache 2</name>
+			<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+			<distribution>repo</distribution>
+			<comments>A business-friendly OSS license</comments>
+		</license>
+	</licenses>
+	<organization>
+		<name>Alibaba</name>
+		<url>http://www.alibaba.com</url>
+	</organization>
+	<properties>
+		<!-- Common libs -->
+		<spring_version>2.5.6.SEC03</spring_version>
+		<javassist_version>3.15.0-GA</javassist_version>
+		<netty_version>3.2.5.Final</netty_version>
+		<mina_version>1.1.7</mina_version>
+		<grizzly_version>2.1.4</grizzly_version>
+		<httpclient_version>4.1.2</httpclient_version>
+		<hessian_lite_version>3.2.1</hessian_lite_version>
+		<xstream_version>1.4.1</xstream_version>
+		<fastjson_version>1.1.8</fastjson_version>
+		<bsf_version>3.1</bsf_version>
+		<zookeeper_version>3.3.3</zookeeper_version>
+		<jedis_version>2.0.0</jedis_version>
+		<jfreechart_version>1.0.13</jfreechart_version>
+		<hessian_version>4.0.7</hessian_version>
+		<servlet_version>2.5</servlet_version>
+		<jetty_version>6.1.26</jetty_version>
+		<validation_version>1.0.0.GA</validation_version>
+		<hibernate_validator_version>4.2.0.Final</hibernate_validator_version>
+		<jcache_version>0.4</jcache_version>
+		<!-- Log libs -->
+		<log4j_version>1.2.16</log4j_version>
+		<slf4j_version>1.6.2</slf4j_version>
+		<!-- Test libs -->
+		<junit_version>4.10</junit_version>
+		<easymock_version>3.0</easymock_version>
+		<jmockit_version>0.999.8</jmockit_version>
+		<!-- Build args -->
+		<argline>-server -Xms128m -Xmx512m -XX:PermSize=64m -XX:MaxPermSize=256m</argline>
+		<skip_maven_deploy>false</skip_maven_deploy>
+		<updateReleaseInfo>true</updateReleaseInfo>
+        <project.build.sourceEncoding>${file_encoding}</project.build.sourceEncoding>
+	</properties>
+	<modules>
+		<module>dubbo-common</module>
+		<module>dubbo-remoting</module>
+		<module>dubbo-remoting-netty</module>
+		<module>dubbo-remoting-mina</module>
+		<module>dubbo-remoting-grizzly</module>
+		<module>dubbo-remoting-p2p</module>
+		<module>dubbo-remoting-http</module>
+		<module>dubbo-rpc</module>
+		<module>dubbo-rpc-default</module>
+		<module>dubbo-rpc-injvm</module>
+		<module>dubbo-rpc-rmi</module>
+		<module>dubbo-rpc-hessian</module>
+		<module>dubbo-validation</module>
+		<module>dubbo-cache</module>
+		<module>dubbo-cluster</module>
+		<module>dubbo-registry</module>
+		<module>dubbo-registry-default</module>
+		<module>dubbo-registry-multicast</module>
+		<module>dubbo-registry-zookeeper</module>
+		<module>dubbo-registry-redis</module>
+		<module>dubbo-monitor</module>
+		<module>dubbo-monitor-default</module>
+		<module>dubbo-config</module>
+		<module>dubbo-container</module>
+		<module>dubbo</module>
+		<module>dubbo-registry-simple</module>
+		<module>dubbo-monitor-simple</module>
+		<module>dubbo-demo</module>
+		<module>dubbo-demo-consumer</module>
+		<module>dubbo-demo-provider</module>
+		<module>dubbo-examples</module>
+	</modules>
+	<dependencyManagement>
+		<dependencies>
+			<!-- Common libs -->
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring</artifactId>
+				<version>${spring_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.javassist</groupId>
+				<artifactId>javassist</artifactId>
+				<version>${javassist_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.netty</groupId>
+				<artifactId>netty</artifactId>
+				<version>${netty_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.mina</groupId>
+				<artifactId>mina-core</artifactId>
+				<version>${mina_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.glassfish.grizzly</groupId>
+				<artifactId>grizzly-core</artifactId>
+				<version>${grizzly_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.httpcomponents</groupId>
+				<artifactId>httpclient</artifactId>
+				<version>${httpclient_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>com.alibaba</groupId>
+				<artifactId>hessian-lite</artifactId>
+				<version>${hessian_lite_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>com.alibaba</groupId>
+				<artifactId>fastjson</artifactId>
+				<version>${fastjson_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>com.thoughtworks.xstream</groupId>
+				<artifactId>xstream</artifactId>
+				<version>${xstream_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.bsf</groupId>
+				<artifactId>bsf-api</artifactId>
+				<version>${bsf_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.zookeeper</groupId>
+				<artifactId>zookeeper</artifactId>
+				<version>${zookeeper_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>redis.clients</groupId>
+				<artifactId>jedis</artifactId>
+				<version>${jedis_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>jfree</groupId>
+				<artifactId>jfreechart</artifactId>
+				<version>${jfreechart_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>com.caucho</groupId>
+				<artifactId>hessian</artifactId>
+				<version>${hessian_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>javax.servlet</groupId>
+				<artifactId>servlet-api</artifactId>
+				<version>${servlet_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.mortbay.jetty</groupId>
+				<artifactId>jetty</artifactId>
+				<version>${jetty_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>javax.validation</groupId>
+				<artifactId>validation-api</artifactId>
+				<version>${validation_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.hibernate</groupId>
+				<artifactId>hibernate-validator</artifactId>
+				<version>${hibernate_validator_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>javax.cache</groupId>
+				<artifactId>cache-api</artifactId>
+				<version>${jcache_version}</version>
+			</dependency>
+			<!-- Log libs -->
+			<dependency>
+				<groupId>log4j</groupId>
+				<artifactId>log4j</artifactId>
+				<version>${log4j_version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.slf4j</groupId>
+				<artifactId>slf4j-api</artifactId>
+				<version>${slf4j_version}</version>
+			</dependency>
+			<!-- Test libs -->
+			<dependency>
+				<groupId>junit</groupId>
+				<artifactId>junit</artifactId>
+				<version>${junit_version}</version>
+				<scope>test</scope>
+			</dependency>
+			<dependency>
+				<groupId>org.easymock</groupId>
+				<artifactId>easymock</artifactId>
+				<version>${easymock_version}</version>
+				<scope>test</scope>
+			</dependency>
+			<dependency>
+				<groupId>com.googlecode.jmockit</groupId>
+				<artifactId>jmockit</artifactId>
+				<version>${jmockit_version}</version>
+				<scope>test</scope>
+			</dependency>
+			<dependency>
+				<groupId>org.easymock</groupId>
+				<artifactId>easymockclassextension</artifactId>
+				<version>${easymock_version}</version>
+				<scope>test</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.easymock</groupId>
+			<artifactId>easymock</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.easymock</groupId>
+			<artifactId>easymockclassextension</artifactId>
+		</dependency>
+			<dependency>
+			<groupId>com.googlecode.jmockit</groupId>
+			<artifactId>jmockit</artifactId>
+		</dependency>
+	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<addMavenDescriptor>true</addMavenDescriptor>
+						<index>true</index>
+						<manifest>
+							<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+						</manifest>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<configuration>
+					<useSystemClassLoader>true</useSystemClassLoader>
+					<testFailureIgnore>true</testFailureIgnore>
+					<forkMode>pertest</forkMode>
+					<argLine>${argline}</argLine>
+					<systemProperties>
+						<!-- common shared -->
+						<property>
+							<name>transporter</name>
+							<value>${transporter}</value>
+						</property>
+						<property>
+							<name>serialization</name>
+							<value>${serialization}</value>
+						</property>
+						<!-- server side -->
+						<property>
+							<name>port</name>
+							<value>${port}</value>
+						</property>
+						<property>
+							<name>threadpool</name>
+							<value>${threadpool}</value>
+						</property>
+						<property>
+							<name>threads</name>
+							<value>${threads}</value>
+						</property>
+						<property>
+							<name>iothreads</name>
+							<value>${iothreads}</value>
+						</property>
+						<!-- client side -->
+						<property>
+							<name>server</name>
+							<value>${server}</value>
+						</property>
+						<property>
+							<name>timeout</name>
+							<value>${timeout}</value>
+						</property>
+						<property>
+							<name>length</name>
+							<value>${length}</value>
+						</property>
+						<property>
+							<name>connections</name>
+							<value>${connections}</value>
+						</property>
+						<property>
+							<name>base</name>
+							<value>${base}</value>
+						</property>
+						<property>
+							<name>concurrent</name>
+							<value>${concurrent}</value>
+						</property>
+						<property>
+							<name>runs</name>
+							<value>${runs}</value>
+						</property>
+						<property>
+							<name>onerror</name>
+							<value>${onerror}</value>
+						</property>
+					</systemProperties>
+				</configuration>
+			</plugin>
+			<plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>${skip_maven_deploy}</skip>
+                </configuration>
+            </plugin>
+		</plugins>
+	</build>
+	<repositories>
+		<repository>
+			<id>opensesame.releases</id>
+			<url>http://code.alibabatech.com/mvn/releases</url>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+	</repositories>
+	<issueManagement>
+		<system>jira</system>
+		<url>http://code.alibabatech.com/jira/browse/DUBBO</url>
+	</issueManagement>
+	<scm>
+		<url>http://code.alibabatech.com/svn/dubbo/trunk</url>
+		<connection>scm:svn:http://code.alibabatech.com/svn/dubbo/trunk</connection>
+	</scm>
+	<mailingLists>
+		<mailingList>
+			<name>Dubbo User Mailling List</name>
+			<subscribe>dubbo-subscribe AT googlegroups DOT com</subscribe>
+			<unsubscribe>dubbo-unsubscribe AT googlegroups DOT com</unsubscribe>
+			<post>dubbo AT googlegroups DOT com</post>
+			<archive>http://groups.google.com/group/dubbo</archive>
+		</mailingList>
+	</mailingLists>
+	<developers>
+		<developer>
+			<name>QianXiao(Shawn)</name>
+			<id>shawn.qianx</id>
+			<email>shawn.qianx (AT) alibaba-inc.com</email>
+			<roles>
+				<role>Developer</role>
+			</roles>
+			<timezone>+8</timezone>
+		</developer>
+		<developer>
+			<name>LiangFei(William)</name>
+			<id>william.liangf</id>
+			<email>william.liangf (AT) alibaba-inc.com</email>
+			<roles>
+				<role>Developer</role>
+			</roles>
+			<timezone>+8</timezone>
+		</developer>
+		<developer>
+			<name>LiDing(Jerry)</name>
+			<id>ding.lid</id>
+			<email>ding.lid (AT) alibaba-inc.com</email>
+			<roles>
+				<role>Developer</role>
+			</roles>
+			<timezone>+8</timezone>
+		</developer>
+		<developer>
+			<name>LiuChao(Charles)</name>
+			<id>chao.liuc</id>
+			<email>chao.liuc (AT) alibaba-inc.com</email>
+			<roles>
+				<role>Developer</role>
+			</roles>
+			<timezone>+8</timezone>
+		</developer>
+		<developer>
+			<name>LiuHaoMin(Ludvik)</name>
+			<id>haoming.liuhm</id>
+			<email>haoming.liuhm (AT) alibaba-inc.com</email>
+			<roles>
+				<role>Developer</role>
+			</roles>
+			<timezone>+8</timezone>
+		</developer>
+		<developer>
+			<name>ChenLei(Tony)</name>
+			<id>tony.chenl</id>
+			<email>tony.chenl (AT) alibaba-inc.com</email>
+			<roles>
+				<role>Developer</role>
+			</roles>
+			<timezone>+8</timezone>
+		</developer>
+		<developer>
+			<name>LuGang(Kimi)</name>
+			<id>gang.lvgm</id>
+			<email>gang.lvgm (AT) alibaba-inc.com</email>
+			<roles>
+				<role>Developer</role>
+			</roles>
+			<timezone>+8</timezone>
+		</developer>
+	</developers>
+</project>