tag 2.0.13

git-svn-id: http://code.alibabatech.com/svn/dubbo/tags/2.0.13@1066 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..a6fda21
--- /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.0.13-assembly.tar.gz
+    cd dubbo-demo-provider-2.0.13/bin
+    ./start.sh
+
+5. Install the demo consumer:
+
+    cd ~/dubbo/dubbo-demo-consumer/target
+    tar zxvf dubbo-demo-consumer-2.0.13-assembly.tar.gz
+    cd dubbo-demo-consumer-2.0.13/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.0.13-assembly.tar.gz
+    cd dubbo-simple-monitor-2.0.13/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.0.13-assembly.tar.gz
+    cd dubbo-simple-registry-2.0.13/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-cluster/pom.xml b/dubbo-cluster/pom.xml
new file mode 100644
index 0000000..22a5129
--- /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.0.13</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..7ef701b
--- /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.Adaptive;

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

+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

+ */

+@Extension(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> merge(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..0f4bc14
--- /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#merge(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..603cfe7
--- /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;
+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#merge(Directory)
+ * @author qian.lei
+ * @author william.liangf
+ */
+@Extension(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/Router.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
new file mode 100644
index 0000000..e0dacd6
--- /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#merge(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..71f96e2
--- /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.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+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#merge(Directory)

+ * @see com.alibaba.dubbo.rpc.cluster.Directory#list(Invocation)
+ * @author chao.liuc
+ */
+@Extension
+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..f9f1f5b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/AbstractDirectory.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.rpc.cluster.directory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+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.Directory;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+/**
+ * 增加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));
+        }
+        if (routers != null) {
+            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(List<Router> r){
+        routers = r;
+    }
+    
+    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/LeastActiveLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java
new file mode 100644
index 0000000..6123071
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.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.loadbalance;

+

+import java.util.List;

+import java.util.Random;

+

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

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

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

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

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

+

+/**

+ * LeastActiveLoadBalance

+ * 

+ * @author william.liangf

+ */

+@Extension(LeastActiveLoadBalance.NAME)

+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..42f6ab7
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.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.loadbalance;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+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.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..a86c5f7
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.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.cluster.loadbalance;
+
+import java.util.List;
+import java.util.Random;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * random load balance.
+ *
+ * @author qianlei
+ * @author william.liangf
+ */
+@Extension(RandomLoadBalance.NAME)
+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 (!sameWeight && totalWeight > 0) {
+            // 如果权重不相同且权重大于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..a8aaec6
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.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.cluster.loadbalance;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.AtomicPositiveInteger;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * Round robin load balance.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+@Extension(RoundRobinLoadBalance.NAME)
+public class RoundRobinLoadBalance extends AbstractLoadBalance {
+    public static final String NAME = "roundrobin"; 
+    
+    private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
+
+    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation) {
+        String key = invokers.get(0).getInterface().getName() + "." + invocation.getMethodName();// + System.identityHashCode(invokers);
+        AtomicPositiveInteger sequence = sequences.get(key);
+        if (sequence == null) {
+            sequences.putIfAbsent(key, new AtomicPositiveInteger());
+            sequence = sequences.get(key);
+        }
+        // 取模轮循
+        return invokers.get(sequence.getAndIncrement() % invokers.size());
+    }
+
+}
\ No newline at end of file
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..aee5f54
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.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.cluster.router;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.IOUtils;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+@Extension(FileRouterFactory.NAME)
+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(RpcConstants.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(RpcConstants.TYPE_KEY, type).addParameterAndEncoded(RpcConstants.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/ScriptRouter.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java
new file mode 100644
index 0000000..4f887ba
--- /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.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.RpcConstants;

+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(RpcConstants.TYPE_KEY);

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

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

+            type = RpcConstants.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..43edc39
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.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.router;

+

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

+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

+ */

+@Extension(ScriptRouterFactory.NAME)

+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..9d71d7b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
@@ -0,0 +1,252 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

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

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

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

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

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

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

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

+

+/**

+ * AbstractRpcRouter

+ * 

+ * @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 ;

+        this.availablecheck = url.getParameter(RpcConstants.CLUSTER_AVAILABLE_CHECK_KEY, RpcConstants.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,RpcConstants.CLUSTER_STICKY_KEY, RpcConstants.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 {

+

+        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.");

+        }

+        

+        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);

+    }

+    

+    @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..44b5f8c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.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.Extension;

+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.cluster.Cluster;

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

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

+

+/**

+ * AvailableCluster

+ * 

+ * @author william.liangf

+ */

+@Extension(AvailableCluster.NAME)

+public class AvailableCluster implements Cluster {

+    

+    public static final String NAME = "available";

+

+    public <T> Invoker<T> merge(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/ClusterUtils.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java
new file mode 100644
index 0000000..b32e003
--- /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..0fbb93e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.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.rpc.cluster.support;

+

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

+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

+ */

+@Extension(FailbackCluster.NAME)

+public class FailbackCluster implements Cluster {

+

+    public final static String NAME = "failback";    

+

+    public <T> Invoker<T> merge(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..992b742
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.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.rpc.cluster.support;

+

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

+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

+ */

+@Extension(FailfastCluster.NAME)

+public class FailfastCluster implements Cluster {

+

+    public final static String NAME = "failfast";

+

+    public <T> Invoker<T> merge(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..92b738f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.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.support;
+
+import java.util.List;

+

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

+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().getAnnotation(Extension.class).value() + " 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..312f188
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.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.rpc.cluster.support;

+

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

+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

+ */

+@Extension(FailoverCluster.NAME)

+public class FailoverCluster implements Cluster {

+

+    public final static String NAME = "failover";

+

+    public <T> Invoker<T> merge(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..57a2dc5
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.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.rpc.cluster.support;

+

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

+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

+ */

+@Extension(FailsafeCluster.NAME)

+public class FailsafeCluster implements Cluster {

+

+    public final static String NAME = "failsafe";

+

+    public <T> Invoker<T> merge(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..65f955f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.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.rpc.cluster.support;

+

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

+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

+ */

+@Extension(ForkingCluster.NAME)

+public class ForkingCluster implements Cluster {

+    

+    public final static String NAME = "forking"; 

+    

+    public <T> Invoker<T> merge(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/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.Cluster b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.Cluster
new file mode 100644
index 0000000..f952821
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.Cluster
@@ -0,0 +1,6 @@
+com.alibaba.dubbo.rpc.cluster.support.FailoverCluster

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

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

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

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

+com.alibaba.dubbo.rpc.cluster.support.AvailableCluster
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.LoadBalance b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.LoadBalance
new file mode 100644
index 0000000..6bf44c0
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.LoadBalance
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.rpc.cluster.loadbalance.LoadBalanceAdptive

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

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

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

+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..f179f21
--- /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.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+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"
+//            +"&"+RpcConstants.CLUSTER_AVAILABLE_CHECK_KEY+"=true"
+            +"&"+RpcConstants.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(RpcConstants.CLUSTER_STICKY_KEY, String.valueOf(check));
+        }else {
+            url = url.addParameter(methodName+"."+RpcConstants.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..ade8587
--- /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.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+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/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..9b79bb2
--- /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.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcConstants;
+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(RpcConstants.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..8ff505c
--- /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.ExtensionLoader;
+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.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 = EasyMock.createMock(Invocation.class);
+        EasyMock.expect(invocation.getMethodName()).andReturn(methodName).anyTimes();
+        EasyMock.replay(invocation);
+    }
+
+    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..a7b09d6
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
@@ -0,0 +1,458 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

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

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

+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<AbstractClusterInvokerTest>> invokers = new ArrayList<Invoker<AbstractClusterInvokerTest>>();
+    List<Invoker<AbstractClusterInvokerTest>> selectedInvokers = new ArrayList<Invoker<AbstractClusterInvokerTest>>();
+    AbstractClusterInvoker<AbstractClusterInvokerTest> cluster;
+    AbstractClusterInvoker<AbstractClusterInvokerTest> cluster_nocheck;
+    Directory<AbstractClusterInvokerTest> dic ;
+    Invocation invocation;
+    URL url = URL.valueOf("registry://localhost:9090");
+    
+    Invoker<AbstractClusterInvokerTest> invoker1 ;
+    Invoker<AbstractClusterInvokerTest> invoker2 ;
+    Invoker<AbstractClusterInvokerTest> invoker3 ;
+    Invoker<AbstractClusterInvokerTest> invoker4 ;
+    Invoker<AbstractClusterInvokerTest> invoker5 ;
+    
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+    @SuppressWarnings({ "unchecked" })
+    @Before
+    public void setUp() throws Exception {
+        dic = EasyMock.createMock(Directory.class);
+        
+        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 turl = URL.valueOf("test://test:11/test");
+        
+        EasyMock.expect(invoker1.isAvailable()).andReturn(false).anyTimes();
+        EasyMock.expect(invoker1.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+        EasyMock.expect(invoker1.getUrl()).andReturn(turl.addParameter("name", "invoker1")).anyTimes();
+        
+        EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker2.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+        EasyMock.expect(invoker2.getUrl()).andReturn(turl.addParameter("name", "invoker2")).anyTimes();
+        
+        EasyMock.expect(invoker3.isAvailable()).andReturn(false).anyTimes();
+        EasyMock.expect(invoker3.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+        EasyMock.expect(invoker3.getUrl()).andReturn(turl.addParameter("name", "invoker3")).anyTimes();
+        
+        EasyMock.expect(invoker4.isAvailable()).andReturn(true).anyTimes();
+        EasyMock.expect(invoker4.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+        EasyMock.expect(invoker4.getUrl()).andReturn(turl.addParameter("name", "invoker4")).anyTimes();
+        
+        EasyMock.expect(invoker5.isAvailable()).andReturn(false).anyTimes();
+        EasyMock.expect(invoker5.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+        EasyMock.expect(invoker5.getUrl()).andReturn(turl.addParameter("name", "invoker5")).anyTimes();
+        
+        EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+        
+        invocation = EasyMock.createMock(Invocation.class);
+        EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+        EasyMock.replay(dic,invoker1,invoker2,invoker3,invoker4,invoker5,invocation);
+        
+        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(RpcConstants.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 testInvoke() {
+//        fail("Not yet implemented");
+    }
+
+    @Test
+    public void testDoInvoke() {
+//        fail("Not yet implemented");
+    }

+    

+

+    @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());

+        }

+    }
+
+    
+}
\ 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..ba47c70
--- /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.assertSame;

+import static org.junit.Assert.assertTrue;

+import static org.junit.Assert.assertFalse;

+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/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..05ab26f
--- /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.0.13</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/Adaptive.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Adaptive.java
new file mode 100644
index 0000000..b806424
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Adaptive.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;
+
+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;
+
+/**
+ * 在{@link ExtensionLoader}生成Extension的Adaptive Instance时,为{@link ExtensionLoader}提供信息。
+ * 
+ * @author ding.lid
+ * 
+ * @see ExtensionLoader
+ * @see URL
+ * @see Extension
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Adaptive {
+    
+    /**
+     * 从{@link URL}的Key名,对应的Value作为要Adapt成的Extension名。
+     * <p>
+     * 如果{@link URL}这些Key都没有Value,使用 用 缺省的扩展(在接口的{@link Extension}中设定的值)。<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 Extension#value()
+     */
+    String[] value() default {};
+    
+}
\ 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..8abfc9b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.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.common;

+

+import java.util.Arrays;

+import java.util.Collections;

+import java.util.List;

+import java.util.regex.Pattern;

+

+/**

+ * Constants

+ * 

+ * @author william.liangf

+ */

+public class Constants {

+

+    public static final List<String> DEFAULT_TELNET_COMMANDS    = Collections.unmodifiableList(Arrays.asList(new String[] {

+            "ls", "ps", "cd", "pwd", "invoke", "count", "trace", "status", "log", "help", "clear", "exit" }));

+

+    public static final List<String> DEFAULT_CHECK_STATUSES    = Collections.unmodifiableList(Arrays.asList(new String[] {

+            "server", "registry", "threadpool", "datasource", "spring", "memory", "load" }));

+    

+    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                 = 5;

+

+    public static final int          DEFAULT_FORKS                  = 2;

+

+    public static final String       DEFAULT_THREAD_NAME            = "Dubbo";

+

+    public static final int          DEFAULT_THREADS                = 100;

+

+    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";

+    

+    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       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       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       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       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 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       RETURN_PREFIX                      = "return ";

+

+    private Constants(){

+    }

+

+}
\ No newline at end of file
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..95ef1e7
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Extension.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;

+

+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 Extension {

+

+    /**

+     * 扩展点名称。<br>

+     * 

+     * 如果注解在扩展的接口上,则缺省的扩展点。<p>

+     */

+	String value() default "";

+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/ExtensionLoader.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/ExtensionLoader.java
new file mode 100644
index 0000000..a323e7b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/ExtensionLoader.java
@@ -0,0 +1,572 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.BufferedReader;

+import java.io.InputStreamReader;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.Arrays;

+import java.util.Collections;

+import java.util.Enumeration;

+import java.util.HashMap;

+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.bytecode.ClassGenerator;

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

+ * @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 Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");

+    

+    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();

+

+    private final Class<?> type;

+    

+    private final Reference<Map<String, Class<?>>> cachedClasses = new Reference<Map<String,Class<?>>>();

+    

+	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>();

+    

+    @SuppressWarnings("unchecked")

+    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {

+        if (type == null)

+            throw new IllegalArgumentException("Extension type == null");

+        

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

+    }

+	

+	@SuppressWarnings("unchecked")

+	public T getExtension(String name) {

+		if (name == null || name.length() == 0)

+		    throw new IllegalArgumentException("Extension name == null");

+		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) {

+	        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()));

+    }

+	

+	/**

+	 * 返回缺省的扩展点名,如果没有设置缺省则返回<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 = injectExtension((T) clazz.newInstance());

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

+            for (Method method : instance.getClass().getMethods()) {

+                if (method.getName().startsWith("set")

+                        && method.getParameterTypes().length == 1

+                        && Modifier.isPublic(method.getModifiers())) {

+                    Class<?> pt = method.getParameterTypes()[0];

+                    if (pt.isInterface() && getExtensionLoader(pt).getSupportedExtensions().size() > 0) {

+                        try {

+                            Object adaptive = getExtensionLoader(pt).getAdaptiveExtension();

+                            method.invoke(instance, adaptive);

+                        } 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 Extension defaultAnnotation = type.getAnnotation(Extension.class);

+        if(defaultAnnotation != null) {

+            String[] names = NAME_SEPARATOR.split(defaultAnnotation.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];

+        }

+        

+        ClassLoader classLoader = findClassLoader();

+        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();

+        String fileName = null;

+        try {

+            fileName = SERVICES_DIRECTORY + type.getName();

+            Enumeration<java.net.URL> urls;

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

+                                        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();

+                                                Extension extension = clazz.getAnnotation(Extension.class);

+                                                if (extension == null) {

+                                                    throw new IllegalStateException("No such @Extension annotation in class " + clazz.getName());

+                                                }

+                                                String name = extension.value();

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

+                                                    throw new IllegalStateException("Illegal @Extension annotation in class " + clazz.getName());

+                                                }

+                                                String[] names = NAME_SEPARATOR.split(name);

+                                                for (String n : names) {

+                                                    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);

+        }

+        return extensionClasses;

+    }

+    

+    @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() {

+        ClassLoader classLoader = findClassLoader();

+        

+        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!");

+        

+        ClassGenerator cg = ClassGenerator.newInstance(classLoader);

+        cg.setClassName(type.getName() + "$Adpative");

+        cg.addInterface(type);

+        cg.addDefaultConstructor();

+        

+        for (Method method : methods) {

+            Class<?> rt = method.getReturnType();

+            Class<?>[] pts = method.getParameterTypes();

+

+            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("if (arg%d == null)  { throw new IllegalArgumentException(\"url == null\"); }",

+                                    urlTypeIndex);

+                    code.append(s);

+                    

+                    s = String.format("%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("if (arg%d == null)  { throw new IllegalArgumentException(\"%s argument == null\"); }",

+                                    urlTypeIndex, pts[urlTypeIndex].getName());

+                    code.append(s);

+                    s = String.format("if (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 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]))

+                                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]))

+                                getNameCode = String.format("url.getParameter(\"%s\")", value[i]);

+                            else

+                                getNameCode = "url.getProtocol()";

+                        }

+                    }

+                    else {

+                        if(!"protocol".equals(value[i]))

+                            getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);

+                        else

+                            getNameCode = String.format("( url.getProtocol() == null ? (%s) : url.getProtocol() )", getNameCode);

+                    }

+                }

+                code.append("String extName = ").append(getNameCode).append(";");

+                // check extName == null?

+                String s = String.format("if(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("%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",

+                        type.getName(), ExtensionLoader.class.getName(), type.getName());

+                code.append(s);

+                

+                // return statement

+                if (!rt.equals(void.class)) {

+                    code.append("return ");

+                }

+

+                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(");");

+            }

+            

+            cg.addMethod(method.getName(), method.getModifiers(), rt, pts,

+                    method.getExceptionTypes(), code.toString());

+        }

+        return cg.toClass();

+    }

+

+    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/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..449132b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.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.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.logger.Logger;

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

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

+

+/**

+ * 兼容2.0.5之前版本

+ * @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..6811d3a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java
@@ -0,0 +1,1183 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;

+    

+    private volatile transient String ip;

+    

+    private final transient Map<String, Number> numbers = new ConcurrentHashMap<String, Number>();

+    

+    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);

+    }

+

+    public double getParameter(String key, double defaultValue) {

+        Number n = numbers.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);

+        numbers.put(key, d);

+        return d;

+    }

+    

+    public float getParameter(String key, float defaultValue) {

+        Number n = numbers.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);

+        numbers.put(key, f);

+        return f;

+    }

+

+    public long getParameter(String key, long defaultValue) {

+        Number n = numbers.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);

+        numbers.put(key, l);

+        return l;

+    }

+

+    public int getParameter(String key, int defaultValue) {

+        Number n = numbers.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);

+        numbers.put(key, i);

+        return i;

+    }

+

+    public short getParameter(String key, short defaultValue) {

+        Number n = numbers.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);

+        numbers.put(key, s);

+        return s;

+    }

+

+    public byte getParameter(String key, byte defaultValue) {

+        Number n = numbers.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);

+        numbers.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) {

+            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 double getMethodParameter(String method, String key, double defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = numbers.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);

+        numbers.put(methodKey, d);

+        return d;

+    }

+

+    public float getMethodParameter(String method, String key, float defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = numbers.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);

+        numbers.put(methodKey, f);

+        return f;

+    }

+

+    public long getMethodParameter(String method, String key, long defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = numbers.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);

+        numbers.put(methodKey, l);

+        return l;

+    }

+

+    public int getMethodParameter(String method, String key, int defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = numbers.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);

+        numbers.put(methodKey, i);

+        return i;

+    }

+

+    public short getMethodParameter(String method, String key, short defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = numbers.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);

+        numbers.put(methodKey, s);

+        return s;

+    }

+

+    public byte getMethodParameter(String method, String key, byte defaultValue) {

+        String methodKey = method + "." + key;

+        Number n = numbers.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);

+        numbers.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 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() {

+    	return 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() {

+		return 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() {

+		return buildString(true, true);

+	}

+

+    public String toFullString(String... parameters) {

+        return buildString(true, true, parameters);

+    }

+    

+    public String toParameterString() {

+        return 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 = getServiceName();

+        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();

+    }

+    

+    public String getServiceName() {

+        return getParameter(Constants.INTERFACE_KEY, path);

+    }

+    

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

+    }

+

+    /**

+     * @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);

+        }

+    }

+    

+}
\ 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..42d1e81
--- /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;
+    }
+    
+    public 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..82d8cae
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.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.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 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/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/json/json.flex b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/json.flex
new file mode 100644
index 0000000..c6d4015
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/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/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..e67b35d
--- /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.Adaptive;

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

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

+

+/**

+ * Serialization. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author ding.lid

+ * @author william.liangf

+ */

+@Extension("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..b111122
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.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.dubbo;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

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

+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

+ */

+@Extension("dubbo")

+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..1c8431d
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.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.common.serialize.support.hessian;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

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

+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

+ */

+@Extension("hessian2")

+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..1535368
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.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.java;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

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

+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

+ */

+@Extension("compactedjava")

+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..58f1a1c
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.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.java;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

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

+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

+ */

+@Extension("java")

+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..5e94197
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.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.common.serialize.support.json;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

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

+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

+ */

+@Extension("fastjson")

+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..590e9df
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.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.common.serialize.support.json;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

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

+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

+ */

+@Extension("json")

+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..c97bad6
--- /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;

+

+/**

+ * StatusChecker

+ * 

+ * @author william.liangf

+ */

+@Extension

+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..dc08083
--- /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;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+

+/**

+ * Load Status

+ * 

+ * @author william.liangf

+ */

+@Extension("load")

+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..c250bf9
--- /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;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+

+/**

+ * MemoryStatus

+ * 

+ * @author william.liangf

+ */

+@Extension("memory")

+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..ceabd645
--- /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.Adaptive;

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

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

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

+

+/**

+ * ThreadPool

+ * 

+ * @author william.liangf

+ */

+@Extension("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..6f8fe45
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.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.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.Extension;

+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

+ */

+@Extension("cached")

+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..24a74b3
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.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.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.Extension;

+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

+ */

+@Extension("fixed")

+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..65ef45f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ConfigUtils.java
@@ -0,0 +1,256 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Arrays;

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

+    			|| "N/A".equalsIgnoreCase(value);

+	}

+	

+	public static boolean isDefault(String value) {

+		return "true".equalsIgnoreCase(value) 

+				|| "default".equalsIgnoreCase(value);

+	}

+	

+	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>();

+        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(defaults);

+            }

+            names.remove(Constants.DEFAULT_KEY);

+        }

+        String[] configs = cfg == null ? new String[0] : Constants.COMMA_SPLIT_PATTERN.split(cfg);

+        for (String config : configs) {

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

+                String[] fs = Constants.COMMA_SPLIT_PATTERN.split(config);

+                names.addAll(Arrays.asList(fs));

+            }

+        }

+        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..09c37b2
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/PojoUtils.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.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.Date;

+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 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 (isBase(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 (isBase(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 ! isBase(cls)

+                && ! Collection.class.isAssignableFrom(cls) 

+                && ! Map.class.isAssignableFrom(cls);

+    }

+

+    private static boolean isBase(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);

+    }

+}
\ 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..ab2a28b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java
@@ -0,0 +1,824 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Method;

+import java.lang.reflect.Modifier;

+import java.net.URL;

+import java.security.CodeSource;

+import java.security.ProtectionDomain;

+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 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<?>>();

+

+	/**

+	 * 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();

+	}

+

+	/**

+	 * 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..7b339a8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/StringUtils.java
@@ -0,0 +1,385 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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;

+

+/**

+ * StringUtils

+ * 

+ * @author qian.lei

+ */

+

+public final class StringUtils

+{

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

+    }

+    

+    /**

+     * 

+     * @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;

+    }

+

+    /**

+     * 

+     * @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();

+	}

+

+	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..affa487
--- /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.getServiceName();

+        String consumerGroup = consumerUrl.getParameter(Constants.GROUP_KEY);

+        String consumerVersion = consumerUrl.getParameter(Constants.VERSION_KEY);

+        String providerInterface = providerUrl.getServiceName();

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

+               && (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/services/com.alibaba.dubbo.common.serialize.Serialization b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.serialize.Serialization
new file mode 100644
index 0000000..09bd8e9
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.serialize.Serialization
@@ -0,0 +1,6 @@
+com.alibaba.dubbo.common.serialize.support.dubbo.DubboSerialization

+com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization

+com.alibaba.dubbo.common.serialize.support.java.JavaSerialization

+com.alibaba.dubbo.common.serialize.support.java.CompactedJavaSerialization

+com.alibaba.dubbo.common.serialize.support.json.JsonSerialization

+com.alibaba.dubbo.common.serialize.support.json.FastJsonSerialization
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..3d14c96
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.common.status.support.MemoryStatusChecker

+com.alibaba.dubbo.common.status.support.LoadStatusChecker
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.threadpool.ThreadPool b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.threadpool.ThreadPool
new file mode 100644
index 0000000..91dae06
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.threadpool.ThreadPool
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool

+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..263ce66
--- /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", "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..a711ed1
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ExtensionLoaderTest.java
@@ -0,0 +1,446 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Map;
+import java.util.Set;
+

+import junit.framework.Assert;

+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+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 {
+        Set<String> exts = ExtensionLoader.getExtensionLoader(ExtensionLoaderTest.class).getSupportedExtensions();
+        assertEquals(0, exts.size());
+    }
+    
+    @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));

+        }

+    }
+}
\ No newline at end of file
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..3744f08
--- /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.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * @author ding.lid
+ */
+@Extension("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..f783345
--- /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(value = "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..2e76cee
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.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.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("impl2")
+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..e3590ec
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.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.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("impl3")
+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..9a29f2d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.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.ext2;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * 无Default
+ * 
+ * @author ding.lid
+ */
+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..2abe4e2
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.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.Extension;
+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
+ */
+@Extension(value = "impl1")
+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..51b8202
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.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.ext2.impl;
+
+import com.alibaba.dubbo.common.Extension;
+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
+ *
+ */
+@Extension("impl2")
+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..f51998f
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.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.ext2.impl;
+
+import com.alibaba.dubbo.common.Extension;
+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
+ *
+ */
+@Extension("impl3")
+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..ecdd3cc
--- /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.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * @author ding.lid
+ */
+@Extension("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..d25174e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.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.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+
+/**
+ * @author ding.lid
+ */
+@Extension(value = "impl1")
+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..678d546
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.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.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl2")
+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..90c30bb
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.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.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension(value="impl3")
+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..76685d3
--- /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.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+
+/**
+ * @author ding.lid
+ */
+@Extension("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..9df9d0b
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.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.ext4.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;
+
+/**
+ * @author ding.lid
+ */
+@Extension(value = "impl1")
+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..f3a8c14
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.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.ext4.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl2")
+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..a347956
--- /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.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * @author ding.lid
+ */
+@Extension("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..762edf0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.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.ext5.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ */
+@Extension("impl1")
+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..f517a04
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.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.ext5.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl2")
+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..f6b0125
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java
@@ -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.

+ */

+package com.alibaba.dubbo.common.extensionloader.ext5.impl;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ */
+@Extension("XXX")
+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..7990f45
--- /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.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * 无Default
+ * 
+ * @author ding.lid
+ */
+@Extension
+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..077e49f
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/DaoImpl.java
@@ -0,0 +1,11 @@
+package com.alibaba.dubbo.common.extensionloader.ext6_inject.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Dao;
+
+@Extension("test")
+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..98ecda3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.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.extensionloader.ext6_inject.impl;
+
+import junit.framework.Assert;

+

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

+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
+ */
+@Extension(value = "impl1")
+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..35ad0cd
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.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.common.extensionloader.ext6_inject.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;
+
+/**
+ * @author ding.lid
+ */
+@Extension("impl2")
+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..d7e1578
--- /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.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * 用于测试:
+ * DUBBO-144 扩展点加载失败(如依赖的三方库运行时没有),如扩展点没有用到,则加载不要报错(在使用到时报错)
+ * 
+ * @author ding.lid
+ */
+@Extension
+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..840196d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7Impl.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.ext7.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext7.Ext7;
+
+/**
+ * @author ding.lid
+ */
+@Extension(value = "ok")
+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..b5663ad
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7InitErrorImpl.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.ext7.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext7.Ext7;
+
+/**
+ * @author ding.lid
+ */
+@Extension(value = "error")
+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..a6ea8bc
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ConfigUtilsTest.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.common.utils;
+
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.util.Arrays;
+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;
+import com.alibaba.dubbo.common.serialize.support.dubbo.DubboSerialization;
+
+/**
+ * @author ding.lid
+ * @author tony.chenl
+ */
+public class ConfigUtilsTest {
+
+    /**
+     * 测试点1:用户配置参数在最后 测试点2:用户配置参数如果带-,会删除同名的默认参数 测试点3:default开头的默认参数会被删除
+     */
+    @Test
+    public void testMergeValues() {
+        List<String> merged = ConfigUtils.mergeValues(Serialization.class, "aaa,bbb,default.cunstom",
+                                                      Arrays.asList(new String[]{"dubbo","default.hessian2","json"}));
+        Assert.assertEquals("[dubbo, json, aaa, bbb, default.cunstom]",merged.toString());
+    }
+    
+    /**
+     * 测试点1:用户配置-default,会删除所有默认参数
+     */
+    @Test
+    public void testMergeValuesDeleteDefault() {
+        List<String> merged = ConfigUtils.mergeValues(DubboSerialization.class, "-default",
+                                                      Arrays.asList("ddd,default.eee,ccc"));
+        Assert.assertEquals("[]", merged.toString());
+    }
+    
+    @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/services/com.alibaba.dubbo.common.threadpool.ThreadPool", false);
+        
+        Properties expected = new Properties();
+        expected.put("com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool", "");
+        expected.put("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/services/com.alibaba.dubbo.common.status.StatusChecker", true);
+        
+        Properties expected = new Properties();
+        expected.put("com.alibaba.dubbo.common.status.support.MemoryStatusChecker", "");
+        expected.put("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..a5f3853
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.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.common.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+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);
+	}
+}
\ 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/services/com.alibaba.dubbo.common.extensionloader.ext1.Ext1 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext1.Ext1
new file mode 100644
index 0000000..ac515d6
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext1.Ext1
@@ -0,0 +1,4 @@
+# Comment 1

+com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl1#Hello World

+com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl2  # Comment 2

+   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/services/com.alibaba.dubbo.common.extensionloader.ext2.Ext2 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext2.Ext2
new file mode 100644
index 0000000..c58f1e4
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext2.Ext2
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl1

+com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl2

+com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl3
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext3.Ext3 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext3.Ext3
new file mode 100644
index 0000000..b0e93dc
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext3.Ext3
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl1

+com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl2

+com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl3
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext4.Ext4 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext4.Ext4
new file mode 100644
index 0000000..a70b49d
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext4.Ext4
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.common.extensionloader.ext4.impl.Ext4Impl1

+com.alibaba.dubbo.common.extensionloader.ext4.impl.Ext4Impl2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod
new file mode 100644
index 0000000..6b655f9
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Impl1

+com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Impl2

+com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper1

+com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6
new file mode 100644
index 0000000..6dd3b3c
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl1

+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/services/com.alibaba.dubbo.common.extensionloader.ext7.Ext7 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext7.Ext7
new file mode 100644
index 0000000..a294fad
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext7.Ext7
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.common.extensionloader.ext7.impl.Ext7InitErrorImpl

+com.alibaba.dubbo.common.extensionloader.ext7.impl.Ext7Impl

diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..82d8158
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/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/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/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..679d0d2
--- /dev/null
+++ b/dubbo-config/pom.xml
@@ -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.

+-->

+<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.0.13</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>org.springframework</groupId>
+			<artifactId>spring</artifactId>
+		</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>
+	</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/com/alibaba/dubbo/config/spring/schema/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..7a2e753
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
@@ -0,0 +1,400 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Map;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

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

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

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

+

+/**

+ * 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_COLON= Pattern.compile("[:\\-._0-9a-zA-Z]+");

+

+    private static final Pattern PATTERN_KEY = Pattern.compile("[*,\\-._0-9a-zA-Z]+");

+

+    protected String id;

+

+    public String getId() {

+        return id;

+    }

+

+    public void setId(String id) {

+        this.id = id;

+    }

+

+    protected static void appendProperties(AbstractConfig config, String prefix) {

+        if (config == null) {

+            return;

+        }

+        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 key = name.substring(3, 4).toLowerCase() + name.substring(4);

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

+                        key = prefix + "." + key;

+                    }

+                    String value = null;

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

+                        value = ConfigUtils.getProperty(key + "." + config.getId());

+                    }

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

+                        value = ConfigUtils.getProperty(key);

+                    }

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

+        }

+        String tag = getTagName(config.getClass());

+        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 = 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 (tag != null && tag.length() > 0) {

+                            String sysval = System.getProperty("dubbo." + tag + "." + prop);

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

+                                str = sysval.trim();

+                            }

+                        }

+                        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) {

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

+                            for (Map.Entry<String, String> entry : map.entrySet()) {

+                                parameters.put(prefix + "." + entry.getKey(), entry.getValue());

+                            }

+                        } else {

+                            parameters.putAll(map);

+                        }

+                    }

+                }

+            } 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 checkNameHasColon(String property, String value) {

+        checkProperty(property, value, MAX_LENGTH, PATTERN_NAME_HAS_COLON);

+    }

+    

+    protected static void checkMultiName(String property, String value) {

+        checkProperty(property, value, MAX_LENGTH, PATTERN_MULTI_NAME);

+    }

+

+    protected static void checkKey(String property, String value) {

+        checkProperty(property, value, MAX_LENGTH, PATTERN_KEY);

+    }

+    

+    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

+            checkNameHasColon(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..b6300cf
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConsumerConfig.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.config;

+

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

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

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

+

+

+/**

+ * 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 ;//= RpcConstants.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 = RpcConstants.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 = RpcConstants.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 = RpcConstants.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..7b2020c
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.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.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;

+

+    // 自定义参数

+    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 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..d18a612
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.java
@@ -0,0 +1,465 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

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

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

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

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

+import com.alibaba.dubbo.rpc.proxy.wrapper.MockReturnInvoker;

+

+/**

+ * 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, "dubbo.registry");

+        }

+    }

+

+    @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, "dubbo.application");

+        

+        String wait = ConfigUtils.getProperty(RpcConstants.SHUTDOWN_TIMEOUT_KEY);

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

+            System.setProperty(RpcConstants.SHUTDOWN_TIMEOUT_KEY, wait.trim());

+        } else {

+            wait = ConfigUtils.getProperty(RpcConstants.SHUTDOWN_TIMEOUT_SECONDS_KEY);

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

+                System.setProperty(RpcConstants.SHUTDOWN_TIMEOUT_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 = System.getProperty("dubbo.registry.address", config.getAddress());

+                if (! RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {

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

+                        throw new IllegalStateException("registry address == null");

+                    }

+                    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, "dubbo.monitor");

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

+        map.put(Constants.INTERFACE_KEY, MonitorService.class.getName());

+        appendParameters(map, monitor);

+        String address = System.getProperty("dubbo.monitor.address", monitor.getAddress());

+        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(RpcConstants.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 {

+                    MockReturnInvoker.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) {

+        checkNameHasColon("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..ab8283d
--- /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) {

+        checkName("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..fcf6840
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.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.config;

+

+import java.util.ArrayList;

+import java.util.List;

+

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

+

+

+/**

+ * 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;

+

+    // 注册中心

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

+    }

+

+    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);

+    }

+

+}
\ 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..d1dab41
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/MethodConfig.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.config;

+

+import java.util.List;

+

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

+

+/**

+ * 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;

+    

+    // 是否需要开启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;

+    }

+    

+    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 = RpcConstants.ON_RETURN_INSTANCE_KEY, excluded = true, attribute = true)

+    public Object getOnreturn() {

+        return onreturn;

+    }

+    

+    public void setOnreturn(Object onreturn) {

+        this.onreturn = onreturn;

+    }

+    

+    @Parameter(key = RpcConstants.ON_RETURN_METHOD_KEY, excluded = true, attribute = true)

+    public String getOnreturnMethod() {

+        return onreturnMethod;

+    }

+

+    public void setOnreturnMethod(String onreturnMethod) {

+        this.onreturnMethod = onreturnMethod;

+    }

+

+    @Parameter(key = RpcConstants.ON_THROW_INSTANCE_KEY, excluded = true, attribute = true)

+    public Object getOnthrow() {

+        return onthrow;

+    }

+

+    public void setOnthrow(Object onthrow) {

+        this.onthrow = onthrow;

+    }

+    

+    @Parameter(key = RpcConstants.ON_THROW_METHOD_KEY, excluded = true, attribute = true)

+    public String getOnthrowMethod() {

+        return onthrowMethod;

+    }

+

+    public void setOnthrowMethod(String onthrowMethod) {

+        this.onthrowMethod = onthrowMethod;

+    }

+    

+    @Parameter(key = RpcConstants.ON_INVOKE_INSTANCE_KEY, excluded = true, attribute = true)

+    public Object getOninvoke() {

+        return oninvoke;

+    }

+    

+    public void setOninvoke(Object oninvoke) {

+        this.oninvoke = oninvoke;

+    }

+    

+    @Parameter(key = RpcConstants.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;

+    }    

+}
\ 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..fd1edbe
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
@@ -0,0 +1,364 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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              server;

+    

+    // 客户端实现

+    private String              client;

+    

+    // 支持的telnet命令,多个命令用逗号分隔

+    private String              telnet;

+

+    // 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;

+    }

+

+    @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;

+    }

+

+    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 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).getSupportedExtensions()) {

+            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocol).destroy();

+        }

+    }

+

+}
\ 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..7830e77
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProviderConfig.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.config;

+

+import java.util.Arrays;

+

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.common.threadpool.ThreadPool;

+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              server;

+    

+    // 客户端实现

+    private String              client;

+    

+    // 支持的telnet命令,多个命令用逗号分隔

+    private String              telnet;

+

+    // 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;

+    }

+

+    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 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..9a089ab
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.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.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.ExtensionLoader;

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

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

+import com.alibaba.dubbo.common.bytecode.Wrapper;

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

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

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

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

+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());

+        if (! generic) {

+            map.put("revision", Version.getVersion(interfaceClass, version));

+            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(),RpcConstants.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(),RpcConstants.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(),RpcConstants.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(RpcConstants.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(RpcConstants.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.merge(new StaticDirectory(u, invokers));

+                }  else { // 不是 注册中心的URL

+                    invoker = cluster.merge(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();

+            String t = ConfigUtils.getProperty("dubbo.service.invoke.timeout");

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

+                consumer.setTimeout(Integer.parseInt(t.trim()));

+            }

+            String r = ConfigUtils.getProperty("dubbo.service.max.retry.providers");

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

+                consumer.setRetries(Integer.parseInt(r.trim()) - 1);

+            }

+            String c = ConfigUtils.getProperty("dubbo.service.allow.no.provider");

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

+                consumer.setCheck(!Boolean.parseBoolean(c));

+            }

+        }

+        appendProperties(consumer, "dubbo.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;

+    }

+    

+    public void setInterface(Class<?> interfaceClass) {

+        if (interfaceClass != null && ! interfaceClass.isInterface()) {

+            throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");

+        }

+        this.interfaceClass = interfaceClass;

+        this.interfaceName = interfaceClass == null ? 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) {

+        checkName("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..4249cae
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
@@ -0,0 +1,294 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+import com.alibaba.dubbo.remoting.Transporter;

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

+

+/**

+ * 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 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(RpcConstants.SHUTDOWN_TIMEOUT_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 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..efc4901
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
@@ -0,0 +1,608 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

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

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

+import com.alibaba.dubbo.common.bytecode.Wrapper;

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

+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 = System.getProperty("dubbo.protocol.name", protocolConfig.getName());

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

+                name = "dubbo";

+            }

+            String host = System.getProperty("dubbo.protocol.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();

+                    }

+                }

+            }

+            String sp = System.getProperty("dubbo.protocol.port");

+            Integer port = (sp != null && sp.length() > 0 ? Integer.valueOf(sp) : 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());

+            appendParameters(map, application);

+            appendParameters(map, provider);

+            appendParameters(map, protocolConfig);

+            appendParameters(map, this);

+            map.put("prompt", "dubbo");

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

+                map.put("revision", Version.getVersion(interfaceClass, version));

+                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(RpcConstants.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, "dubbo.provider");

+    }

+

+    private void checkProtocol() {

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

+                && provider != null) {

+            setProtocols(provider.getProtocols());

+        }

+    	// 兼容旧版本

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

+            ProtocolConfig protocolConfig = new ProtocolConfig();

+            String p = ConfigUtils.getProperty("dubbo.service.protocol");

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

+                protocolConfig.setName(p);

+            }

+            String h = ConfigUtils.getProperty("dubbo.service.server.host");

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

+                protocolConfig.setHost(h);

+            }

+            String o = ConfigUtils.getProperty("dubbo.service.server.port");

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

+                protocolConfig.setPort(Integer.parseInt(o.trim()));

+            }

+            String t = ConfigUtils.getProperty("dubbo.service.max.thread.pool.size");

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

+                protocolConfig.setThreads(Integer.parseInt(t.trim()));

+            }

+            setProtocol(protocolConfig);

+        }

+        for (ProtocolConfig protocolConfig : protocols) {

+            appendProperties(protocolConfig, "dubbo.protocol");

+        }

+    }

+

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

+    }

+    

+    public void setInterface(Class<?> interfaceClass) {

+        if (interfaceClass != null && ! interfaceClass.isInterface()) {

+            throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");

+        }

+        this.interfaceClass = interfaceClass;

+        this.interfaceName = interfaceClass == null ? 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..db1207e
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.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.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;

+
+/**
+ * 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;
+	}
+    
+    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..5f1e030
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.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.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;

+
+/**
+ * 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;
+		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/schema/DubboBeanDefinitionParser.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
new file mode 100644
index 0000000..69b2bbb
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
@@ -0,0 +1,366 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.Node;

+import org.w3c.dom.NodeList;

+

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

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

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

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

+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);
+    }
+
+    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);
+        }
+        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));
+                    }
+                }
+            }
+        }
+        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 = name.substring(3, 4).toLowerCase() + name.substring(4);
+                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)) {
+                    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;
+                                    if (! "id".equals(property) 
+                                            && ! "name".equals(property) 
+                                            && ! "interface".equals(property)) {
+                                        String sysProperty = element.getPrefix() + "." + element.getLocalName() + "." + id + "." + property;
+                                        String sysValue = System.getProperty(sysProperty);
+                                        if (sysValue != null && sysValue.trim().length() > 0) {
+                                            reference = sysValue.trim();
+                                        } else {
+                                            sysProperty = element.getPrefix() + "." + element.getLocalName() + "." + property;
+                                            sysValue = System.getProperty(sysProperty);
+                                            if (sysValue != null && sysValue.trim().length() > 0) {
+                                                reference = sysValue.trim();
+                                            }
+                                        }
+                                    }
+                                } 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);
+                            }
+                    	}
+                    }
+                }
+            }
+        }
+        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 void 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));
+                    }
+                }
+            }
+            if (parameters != null) {
+                beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
+            }
+        }
+    }
+
+    @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..1e756d7
--- /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;

+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

+ */

+@Extension("datasource")

+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..89675fc
--- /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;

+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

+ */

+@Extension("spring")

+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/services/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-config/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..4c153f9
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.config.spring.status.SpringStatusChecker

+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..364e467
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1 @@
+http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=com/alibaba/dubbo/config/spring/schema/dubbo.xsd
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/com/alibaba/dubbo/config/spring/schema/dubbo.xsd b/dubbo-config/src/main/resources/com/alibaba/dubbo/config/spring/schema/dubbo.xsd
new file mode 100644
index 0000000..f9088d6
--- /dev/null
+++ b/dubbo-config/src/main/resources/com/alibaba/dubbo/config/spring/schema/dubbo.xsd
@@ -0,0 +1,918 @@
+<?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="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="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: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="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: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: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="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="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: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: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="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="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: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:extension>

+			</xsd:complexContent>

+		</xsd:complexType>

+	</xsd:element>

+	

+</xsd:schema>
\ 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..b1cb102
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/ConfigTest.java
@@ -0,0 +1,568 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.hamcrest.core.IsNot.not;

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

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

+

+/**

+ * ConfigTest

+ * 

+ * @author william.liangf

+ */

+public class ConfigTest {

+    

+    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();

+        DemoService demoService = refer("dubbo://127.0.0.1:20881");

+        String hello = demoService.sayName("hello");

+        assertEquals("say:hello", hello);

+        ctx.stop();

+        ctx.close();

+    }

+

+    @Test

+    public void testMultiProtocolDefault() {

+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-default.xml");

+        ctx.start();

+        DemoService demoService = refer("rmi://127.0.0.1:10991");

+        String hello = demoService.sayName("hello");

+        assertEquals("say:hello", hello);

+        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.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"));

+                    assertThat(expected.getMessage(), not(containsString("timeout")));

+                }

+            } finally {

+                ctx.stop();

+                ctx.close();

+            }

+        } finally {

+            providerContext.stop();

+            providerContext.close();

+        }

+    }

+    

+    @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 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..4012f1b
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/GenericServiceTest.java
@@ -0,0 +1,75 @@
+/*

+ * 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 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.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");

+                }

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

+                Assert.assertEquals("Generic Haha", demoService.sayName("Haha"));

+                try {

+                    demoService.throwDemoException();

+                    Assert.fail();

+                } catch (DemoException e) {

+                    Assert.assertEquals("Generic", e.getMessage());

+                }

+            } 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..df03740
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/Box.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *  
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *  
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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..71dcaba
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoService.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.api;
+
+
+/**
+ * DemoService
+ * 
+ * @author william.liangf
+ */
+public interface DemoService {
+    

+    String sayName(String name);

+    

+    Box getBox();

+    

+    void throwDemoException() throws DemoException;

+    
+}
\ No newline at end of file
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..3cb9b7a
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl.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.config.provider.impl;
+
+import com.alibaba.dubbo.config.api.Box;

+import com.alibaba.dubbo.config.api.DemoException;

+import com.alibaba.dubbo.config.api.DemoService;

+
+/**
+ * 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");

+    }

+    
+}
\ 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..6053d84
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl_LongWaiting.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.config.provider.impl;
+
+import com.alibaba.dubbo.config.api.Box;
+import com.alibaba.dubbo.config.api.DemoException;
+import com.alibaba.dubbo.config.api.DemoService;
+
+/**
+ * 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");
+    }
+    
+}
\ 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..b670859
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBoxDemoServiceImpl.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.config.provider.impl;
+
+import com.alibaba.dubbo.config.api.Box;
+import com.alibaba.dubbo.config.api.DemoException;
+import com.alibaba.dubbo.config.api.DemoService;
+
+/**
+ * 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");
+    }
+    
+}
\ No newline at end of file
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..3905a48
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.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.config.support;
+
+import com.alibaba.dubbo.common.Extension;
+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
+ *
+ */
+@Extension("mockprotocol")
+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..7116d73
--- /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) {
+        
+    }
+
+    /* 
+     * @see com.alibaba.dubbo.registry.RegistryService#unregister(com.alibaba.dubbo.common.URL)
+     */
+    public void unregister(URL url) {
+        
+    }
+
+    /* 
+     * @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..421e4b0
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.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.config.support;
+
+import com.alibaba.dubbo.common.Extension;
+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
+ *
+ */
+@Extension("mockregistry")
+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..f22f80e
--- /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();
+        
+        refConf.setApplication(appConfForReference);
+        consumerConf.setApplication(appConfForConsumer);
+        
+        refConf.setRegistry(regConfForReference);
+        consumerConf.setRegistry(regConfForConsumer);
+        
+        refConf.setConsumer(consumerConf);
+        
+        refConf.setMethods(Arrays.asList(new MethodConfig[]{methodConfForReference}));
+        
+        methodConfForReference.setName("sayName");
+        regConfForReference.setAddress("127.0.0.1:9090");
+        regConfForReference.setProtocol("mockregistry");
+        appConfForReference.setName("ConfigTests");
+        refConf.setInterface("com.alibaba.dubbo.config.api.DemoService");
+    }
+    
+    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/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..6095da6
--- /dev/null
+++ b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+com.alibaba.dubbo.config.support.MockRegistryFactory

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..53472a6
--- /dev/null
+++ b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+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..a7814c2
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-registry.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 id="reg1" address="127.0.0.1:4545" />

+    

+    <dubbo:registry id="reg2" address="127.0.0.1:4546" />

+     

+    <!-- 暴露服务配置 -->

+    <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..75b67c5
--- /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" />

+    

+    <!-- 暴露服务配置 -->

+    <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/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/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..bcbb33f
--- /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.0.13</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..f64c46a
--- /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;

+

+/**

+ * Container. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@Extension("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..704e31f
--- /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.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..ca58274
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.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.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.Extension;

+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

+ */

+@Extension("jetty")

+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..6bd7ce1
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.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.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.Extension;

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

+import com.alibaba.dubbo.container.Container;

+

+/**

+ * Log4jContainer. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@Extension("log4j")

+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..73c2340
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Page.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.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, 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..f6cc1bf
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageHandler.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.container.page;

+

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

+

+/**

+ * PageHandler

+ * 

+ * @author william.liangf

+ */

+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..e6c9ea5
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java
@@ -0,0 +1,287 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Extension;

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

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

+

+/**

+ * 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(handler.getClass().getAnnotation(Extension.class).value(), 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 = pageHandler.getClass().getAnnotation(Extension.class).value();

+                            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 = handler.getClass().getAnnotation(Extension.class).value();

+            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..605d362
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.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.container.page.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+

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

+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.container.page.PageServlet;

+

+/**

+ * HomePageHandler

+ * 

+ * @author william.liangf

+ */

+@Menu(name = "Home", desc = "Home page.", order = Integer.MIN_VALUE)

+@Extension("index")

+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 = handler.getClass().getAnnotation(Extension.class).value();

+            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..90d0844
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.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.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.Extension;

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

+@Extension("log")

+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..51c924b
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.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.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.Extension;

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

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

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

+@Extension("status")

+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..2a2ae26
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.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.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.Extension;

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

+@Extension("system")

+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..2a3e5f3
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.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.container.spring;

+

+import org.springframework.context.support.ClassPathXmlApplicationContext;

+

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

+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

+ */

+@Extension("spring")

+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/services/com.alibaba.dubbo.container.Container b/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..3db2286
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.Container
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.container.spring.SpringContainer

+com.alibaba.dubbo.container.jetty.JettyContainer

+com.alibaba.dubbo.container.log4j.Log4jContainer
\ No newline at end of file
diff --git a/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler b/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..2b48dc7
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.container.page.pages.HomePageHandler

+com.alibaba.dubbo.container.page.pages.StatusPageHandler

+com.alibaba.dubbo.container.page.pages.LogPageHandler

+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..40c4e14
--- /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.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..bba70cb
--- /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.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..d45c2b9
--- /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.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..cbc8b06
--- /dev/null
+++ b/dubbo-demo-consumer/pom.xml
@@ -0,0 +1,114 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - Licensed under the Apache License, Version 2.0 (the "License");

+ - you may not use this file except in compliance with the License.

+ - You may obtain a copy of the License at

+ -  

+ -      http://www.apache.org/licenses/LICENSE-2.0

+ -  

+ - Unless required by applicable law or agreed to in writing, software

+ - distributed under the License is distributed on an "AS IS" BASIS,

+ - WITHOUT 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.0.13</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>

+		<eclipse.useProjectReferences>false</eclipse.useProjectReferences>
+	</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..2f85049
--- /dev/null
+++ b/dubbo-demo-consumer/src/main/assembly/conf/dubbo.properties
@@ -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.

+##

+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=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..0c529ee
--- /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(1000);

+            } 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..9bcaa9a
--- /dev/null
+++ b/dubbo-demo-consumer/src/test/resources/dubbo.properties
@@ -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.

+##

+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=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..7e504db
--- /dev/null
+++ b/dubbo-demo-provider/pom.xml
@@ -0,0 +1,114 @@
+<!--

+ - Copyright 1999-2011 Alibaba Group.

+ -  

+ - Licensed under the Apache License, Version 2.0 (the "License");

+ - you may not use this file except in compliance with the License.

+ - You may obtain a copy of the License at

+ -  

+ -      http://www.apache.org/licenses/LICENSE-2.0

+ -  

+ - Unless required by applicable law or agreed to in writing, software

+ - distributed under the License is distributed on an "AS IS" BASIS,

+ - WITHOUT 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.0.13</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>

+		<eclipse.useProjectReferences>false</eclipse.useProjectReferences>
+	</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..ac3debc
--- /dev/null
+++ b/dubbo-demo-provider/src/main/assembly/conf/dubbo.properties
@@ -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.

+##

+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=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..7b26842
--- /dev/null
+++ b/dubbo-demo-provider/src/test/resources/dubbo.properties
@@ -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.

+##

+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=dubbo://127.0.0.1:9090

+dubbo.monitor.protocol=registry

+dubbo.protocol.name=dubbo

+dubbo.protocol.port=20881

+#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..80a57e3
--- /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.0.13</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..cb92d95
--- /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-monitor-default/pom.xml b/dubbo-monitor-default/pom.xml
new file mode 100644
index 0000000..20a444c
--- /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.0.13</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..096eab3
--- /dev/null
+++ b/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.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.monitor.dubbo;

+

+import java.text.SimpleDateFormat;

+import java.util.Date;

+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 = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());

+        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..88a5a00
--- /dev/null
+++ b/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.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.dubbo;

+

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

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

+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

+ */

+@Extension("dubbo")

+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..5b80fef
--- /dev/null
+++ b/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.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.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);

+        this.server = url.getParameter(MonitorService.PROVIDER);

+    }

+

+    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 + ((url == null) ? 0 : url.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 (url == null) {

+            if (other.url != null)

+                return false;

+        } else if (!url.equals(other.url))

+            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/services/com.alibaba.dubbo.monitor.MonitorFactory b/dubbo-monitor-default/src/main/resources/META-INF/services/com.alibaba.dubbo.monitor.MonitorFactory
new file mode 100644
index 0000000..94cedc1
--- /dev/null
+++ b/dubbo-monitor-default/src/main/resources/META-INF/services/com.alibaba.dubbo.monitor.MonitorFactory
@@ -0,0 +1 @@
+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..b3a2ed8
--- /dev/null
+++ b/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java
@@ -0,0 +1,131 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

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

+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=1");

+        }

+        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(statistics, lastStatistics);

+        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..ff1cade
--- /dev/null
+++ b/dubbo-monitor-simple/pom.xml
@@ -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.

+-->

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

+		<eclipse.useProjectReferences>false</eclipse.useProjectReferences>
+	</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..5c2c64b
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/assembly/conf/dubbo.properties
@@ -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.

+##

+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=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..ff7dc32
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/RegistryContainer.java
@@ -0,0 +1,342 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Extension;

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

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

+ */

+@Extension("registry")

+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.getServiceName();

+                    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..78ff4a8
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorService.java
@@ -0,0 +1,368 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Executors;

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

+

+/**

+ * SimpleMonitorService

+ * 

+ * @author william.liangf

+ */

+public class SimpleMonitorService implements MonitorService {

+    

+    private static final String[] types = {SUCCESS, FAILURE, ELAPSED, CONCURRENT, MAX_ELAPSED, MAX_CONCURRENT};

+    

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

+

+    // 定时任务执行器

+    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryReconnectTimer", true));

+

+    // 图表绘制定时器

+    private ScheduledFuture<?> chartFuture;

+

+    private String statisticsDirectory = "statistics";

+

+    private String chartsDirectory = "charts";

+    

+    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() {

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

+            public void run() {

+                try {

+                    draw(); // 绘制图表

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

+                    logger.error("Unexpected error occur at reconnect, cause: " + t.getMessage(), t);

+                }

+            }

+        }, 1, 300, TimeUnit.SECONDS);

+        INSTANCE = this;

+    }

+

+    public void close() {

+        chartFuture.cancel(true);

+    }

+

+    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) {

+        try {

+            Date now = new Date();

+            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.getServiceName() 

+                            + "/" + 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);

+                }

+            }

+        } catch (Throwable t) {

+            logger.error(t.getMessage(), t);

+        }

+    }

+

+}
\ 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..05aa7ef
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ApplicationsPageHandler.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.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Set;

+

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

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

+@Extension("applications")

+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 + "\">Depend 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 + ")", "Depend 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..5811e16
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ChartsPageHandler.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.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.Extension;

+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

+ */

+@Extension("charts")

+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..54925eb
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ConsumersPageHandler.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.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+

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

+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

+ */

+@Extension("consumers")

+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>();

+                    row.add(u.toFullString().replace("&", "&amp;"));

+                    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:" }, 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>();

+                    row.add(u.toFullString().replace("&", "&amp;"));

+                    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:" }, 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>();

+                    row.add(u.toFullString().replace("&", "&amp;"));

+                    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 + "\">Depend On</a> | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>", "Consumers (" + rows.size() + ")",

+                    new String[] { "Consumer URL:" }, 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..6b716d4
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/DependenciesPageHandler.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.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Set;

+

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

+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

+ */

+@Extension("dependencies")

+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 + "\">Depend On</a> | Used By" 

+                        : "Depend On | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>"), (reverse ? "Used By" : "Depend 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..96d74a4
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/HostsPageHandler.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.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.Extension;

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

+@Extension("hosts")

+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..dce3d01
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ProvidersPageHandler.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.monitor.simple.pages;

+

+import java.util.ArrayList;

+import java.util.List;

+

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

+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

+ */

+@Extension("providers")

+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>();

+                    row.add(u.toFullString().replace("&", "&amp;"));

+                    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:" }, 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>();

+                    row.add(u.toFullString().replace("&", "&amp;"));

+                    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:" }, 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>();

+                    row.add(u.toFullString().replace("&", "&amp;"));

+                    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 + "\">Depend On</a> | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>", "Providers (" + rows.size() + ")",

+                    new String[] { "Provider URL:" }, 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..a095db7
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ServicesPageHandler.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.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.Extension;

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

+@Extension("services")

+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..0e38442
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/StatisticsPageHandler.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.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.Extension;

+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

+ */

+@Extension("statistics")

+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/resources/META-INF/services/com.alibaba.dubbo.container.Container b/dubbo-monitor-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..84e385c
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.container.Container
@@ -0,0 +1 @@
+com.alibaba.dubbo.monitor.simple.RegistryContainer
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler b/dubbo-monitor-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..30524fa
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,8 @@
+com.alibaba.dubbo.monitor.simple.pages.ServicesPageHandler

+com.alibaba.dubbo.monitor.simple.pages.ProvidersPageHandler

+com.alibaba.dubbo.monitor.simple.pages.ConsumersPageHandler

+com.alibaba.dubbo.monitor.simple.pages.StatisticsPageHandler

+com.alibaba.dubbo.monitor.simple.pages.ChartsPageHandler

+com.alibaba.dubbo.monitor.simple.pages.ApplicationsPageHandler

+com.alibaba.dubbo.monitor.simple.pages.DependenciesPageHandler

+com.alibaba.dubbo.monitor.simple.pages.HostsPageHandler
\ 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..6b3fc36
--- /dev/null
+++ b/dubbo-monitor-simple/src/test/resources/dubbo.properties
@@ -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.

+##

+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=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..e6c66cd
--- /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.0.13</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..f072bf5
--- /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.Adaptive;

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

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

+

+/**

+ * MonitorFactory. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@Extension("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..4b0fbec
--- /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.Extension;

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

+ */

+@Extension("monitor")

+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/services/com.alibaba.dubbo.rpc.Filter b/dubbo-monitor/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..de66615
--- /dev/null
+++ b/dubbo-monitor/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+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..211ded4
--- /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.0.13</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..23ee74d
--- /dev/null
+++ b/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.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.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;

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

+

+/**

+ * 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(RpcConstants.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);

+    }

+    

+    protected void doUnregister(URL url) {

+        registryService.unregister(url);

+    }

+

+    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..b51dc11
--- /dev/null
+++ b/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.java
@@ -0,0 +1,104 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Extension;

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

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

+

+/**

+ * DubboRegistryFactory

+ * 

+ * @author william.liangf

+ */

+@Extension("dubbo")

+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(RpcConstants.REFER_KEY, url.toParameterString()));

+        Invoker<RegistryService> registryInvoker = cluster.merge(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(RpcConstants.EXPORT_KEY).removeParameter(RpcConstants.REFER_KEY)

+                .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())

+                .addParameter(RpcConstants.CLUSTER_STICKY_KEY, "true")

+                .addParameter(RpcConstants.LAZY_CONNECT_KEY, "true")

+                .addParameter(Constants.RECONNECT_KEY, "false")

+                .addParameterIfAbsent(Constants.TIMEOUT_KEY, "10000")

+                .addParameterIfAbsent(RpcConstants.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(RpcConstants.STUB_EVENT_KEY, Boolean.TRUE.toString()) //for event dispatch

+                //.addParameter(RpcConstants.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/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-default/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..403c36f
--- /dev/null
+++ b/dubbo-registry-default/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+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..6257d06
--- /dev/null
+++ b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.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.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) {
+	}
+
+	public boolean isOpen() {
+		return false;
+	}
+
+	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..62249e8
--- /dev/null
+++ b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
@@ -0,0 +1,629 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

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

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

+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(RpcConstants.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(RpcConstants.REFER_KEY), 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(RpcConstants.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));

+        }

+    }

+

+    /**

+     * 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), 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(RpcConstants.ROUTE_PROTOCOL + "://127.0.0.1:9096/");

+        URL routerurl2 = URL.valueOf(RpcConstants.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(RpcConstants.TYPE_KEY, "javascript").addParameter(RpcConstants.ROUTER_KEY,

+                                                                                                 "notsupported").addParameter(RpcConstants.RULE_KEY,

+                                                                                                                              "function test1(){}"));

+        serviceUrls.add(routerurl2.addParameter(RpcConstants.TYPE_KEY, "javascript").addParameter(RpcConstants.ROUTER_KEY,

+                                                                                                  ScriptRouterFactory.NAME).addParameter(RpcConstants.RULE_KEY,

+                                                                                                                                         "function test1(){}"));

+

+        registryDirectory.notify(serviceUrls);

+        List<Router> routers = registryDirectory.getRouters();

+        Assert.assertEquals(1, routers.size());

+        Assert.assertEquals(ScriptRouter.class, routers.get(0).getClass());

+

+        registryDirectory.notify(new ArrayList<URL>());

+        routers = registryDirectory.getRouters();

+        Assert.assertEquals(1, routers.size());

+        Assert.assertEquals(ScriptRouter.class, routers.get(0).getClass());

+

+        serviceUrls.clear();

+        serviceUrls.add(routerurl.addParameter(RpcConstants.ROUTER_KEY, RpcConstants.ROUTER_TYPE_CLEAR));

+        registryDirectory.notify(serviceUrls);

+        routers = registryDirectory.getRouters();

+        Assert.assertEquals(0, routers.size());

+    }

+

+    @Test

+    public void testNotifyRouterUrls_Clean() {

+        if (isScriptUnsupported) return;

+        RegistryDirectory registryDirectory = getRegistryDirectory();

+        URL routerurl = URL.valueOf(RpcConstants.ROUTE_PROTOCOL + "://127.0.0.1:9096/").addParameter(RpcConstants.ROUTER_KEY,

+                                                                                                     "javascript").addParameter(RpcConstants.RULE_KEY,

+                                                                                                                                "function test1(){}").addParameter(RpcConstants.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, routers.size());

+

+        serviceUrls.clear();

+        serviceUrls.add(routerurl.addParameter(RpcConstants.ROUTER_KEY, RpcConstants.ROUTER_TYPE_CLEAR));

+        registryDirectory.notify(serviceUrls);

+        routers = registryDirectory.getRouters();

+        Assert.assertEquals(0, routers.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) {

+

+        }

+

+        public void unregister(URL url) {

+

+        }

+

+        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..1cff45e
--- /dev/null
+++ b/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.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.registry.dubbo;

+

+import static org.junit.Assert.assertEquals;

+

+import org.junit.Test;

+

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

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

+import com.alibaba.dubbo.registry.RegistryFactory;

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

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

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

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

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * RegistryProtocolTest

+ * 

+ * @author tony.chenl

+ */

+public class RegistryProtocolTest {

+

+    static {

+        SimpleRegistryExporter.exportIfAbsent(9090);

+    }

+

+    String service     = "com.alibaba.dubbo.registry.protocol.DemoService:1.0.0";

+    String serviceUrl  = "dubbo://127.0.0.1:9453/" + service + "?notify=true&methods=test1,test2";

+    URL    registryUrl = URL.valueOf("dubbo://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);

+        registryUrl = registryUrl.addParameter(RpcConstants.EXPORT_KEY, serviceUrl);

+        DubboInvoker<DemoService> invoker = new DubboInvoker<DemoService>(DemoService.class,

+                registryUrl, new ExchangeClient[] { new MockedClient("10.20.20.20", 2222, true) });

+        Exporter<DemoService> exporter = registryProtocol.export(invoker);

+        assertEquals(invoker, exporter.getInvoker());

+        exporter.unexport();

+    }

+

+}
\ 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..25cd77a
--- /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.ExtensionLoader;

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

+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..9a39312
--- /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.0.13</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..5db9367
--- /dev/null
+++ b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
@@ -0,0 +1,310 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 static final String REGISTER = "register";

+

+    private static final String UNREGISTER = "unregister";

+

+    private static final String SUBSCRIBE = "subscribe";

+

+    private static final String UNSUBSCRIBE = "unsubscribe";

+    

+    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 (IOException 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(REGISTER)) {

+            URL url = URL.valueOf(msg.substring(REGISTER.length()).trim());

+            registered(url);

+        } else if (msg.startsWith(UNREGISTER)) {

+            URL url = URL.valueOf(msg.substring(UNREGISTER.length()).trim());

+            unregistered(url);

+        } else if (msg.startsWith(SUBSCRIBE)) {

+            URL url = URL.valueOf(msg.substring(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(REGISTER + " " + u.toFullString(), host);

+                    } else {

+                        broadcast(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(REGISTER + " " + url.toFullString());

+    }

+

+    protected void doUnregister(URL url) {

+        broadcast(UNREGISTER + " " + url.toFullString());

+    }

+

+    protected void doSubscribe(URL url, NotifyListener listener) {

+        if (! Constants.ANY_VALUE.equals(url.getServiceName())

+                && url.getParameter(Constants.REGISTER_KEY, true)) {

+            register(url);

+        }

+        broadcast(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.getServiceName())

+                && url.getParameter(Constants.REGISTER_KEY, true)) {

+            unregister(url);

+        }

+        broadcast(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);

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

+                    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);

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

+                    for (NotifyListener listener : entry.getValue()) {

+                        notify(subscribe, listener, list);

+                    }

+                }

+            }

+        }

+    }

+

+    protected void subscribed(URL url, NotifyListener listener) {

+        List<URL> urls = lookup(url);

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

+            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) {

+        super.register(url);

+        registered(url);

+    }

+

+    public void unregister(URL url) {

+        super.unregister(url);

+        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..eb065e2
--- /dev/null
+++ b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.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.registry.multicast;

+

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

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

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+

+/**

+ * MulticastRegistryLocator

+ * 

+ * @author william.liangf

+ */

+@Extension("multicast")

+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/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-multicast/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..e37727e
--- /dev/null
+++ b/dubbo-registry-multicast/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+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..3e1f8f0
--- /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);

+    }

+

+    @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)}.

+     */

+    @Test

+    public void testRegister() {

+        Set<String> registered = null;

+        // clear first

+        registered = registry.getRegistered();

+

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

+            registry.register(serviceUrl);

+            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-simple/pom.xml b/dubbo-registry-simple/pom.xml
new file mode 100644
index 0000000..f372c2b
--- /dev/null
+++ b/dubbo-registry-simple/pom.xml
@@ -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.

+-->

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

+		<eclipse.useProjectReferences>false</eclipse.useProjectReferences>
+	</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..ec0d5ca
--- /dev/null
+++ b/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/simple/SimpleRegistryService.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.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) {

+        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);

+        registered(url);

+    }

+

+    public void unregister(URL url) {

+        String client = RpcContext.getContext().getRemoteAddressString();

+        Set<String> urls = remoteRegistered.get(client);

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

+            urls.remove(url.toFullString());

+        }

+        super.unregister(url);

+        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);

+            }

+        }

+        if (! Constants.ANY_VALUE.equals(url.getServiceName())

+                && url.getParameter(Constants.REGISTER_KEY, true)) {

+            register(url);

+        }

+        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.getServiceName())

+                && url.getParameter(Constants.REGISTER_KEY, true)) {

+            unregister(url);

+        }

+        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);

+    }

+

+    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);

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

+                    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);

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

+                    for (NotifyListener listener : entry.getValue()) {

+                        notify(subscribe, listener, list);

+                    }

+                }

+            }

+        }

+    }

+

+    protected void subscribed(URL url, NotifyListener listener) {

+        List<URL> urls = lookup(url);

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

+            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));

+            }

+        }

+        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/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..403c36f
--- /dev/null
+++ b/dubbo-registry-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+com.alibaba.dubbo.registry.dubbo.DubboRegistryFactory
\ 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..cc22932
--- /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.0.13</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..ca6e286
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java
@@ -0,0 +1,490 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 int DEFAULT_SESSION_TIMEOUT = 60 * 1000;

+    

+    private final static String SEPARATOR = "/";

+

+    private final static String DEFAULT_ROOT = "dubbo";

+

+    private final static String PROVIDERS = "providers";

+

+    private final static String CONSUMERS = "consumers";

+

+    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(SEPARATOR)) {

+            group = 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.TIMEOUT_KEY, 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 = PROVIDERS;

+                        String service = path;

+                        if (service.startsWith(dir)) {

+                            service = service.substring(dir.length());

+                        }

+                        int i = service.indexOf(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(SEPARATOR) + 1) + (CONSUMERS.equals(action) ? PROVIDERS : CONSUMERS));

+                                    adminChildren.addAll(children);

+                                }

+                                notifies = adminChildren;

+                            } else if (CONSUMERS.equals(action)) {

+                                continue;

+                            }

+                            String subscribeService = subscribe.getServiceName();

+                            if (service.equals(subscribeService)) {

+                                List<URL> list = toUrls(subscribe, notifies);

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

+                                    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 && ! 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.getServiceName())) {

+                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);

+                }

+                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);

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

+                    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.getServiceName())) {

+            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);

+        }

+    }

+    

+    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(SEPARATOR)) {

+            return root;

+        }

+        return root + SEPARATOR;

+    }

+    

+    private String toRootPath() {

+        return root;

+    }

+    

+    private String toServicePath(URL url) {

+        String name = url.getServiceName();

+        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) + SEPARATOR + PROVIDERS;

+    }

+

+    private String toSubscribePath(URL url) {

+        return toServicePath(url) + SEPARATOR + CONSUMERS;

+    }

+

+    private String toProviderPath(URL url) {

+        return toCategoryPath(url) + 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..cd4daeb
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.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.registry.zookeeper;

+

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

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

+import com.alibaba.dubbo.registry.Registry;

+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;

+

+/**

+ * ZookeeperRegistryFactory.

+ * 

+ * @author william.liangf

+ */

+@Extension("zookeeper")

+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/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-zookeeper/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..b538326
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+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..79f89a0
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/test/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryTest.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.registry.zookeeper;

+

+import junit.framework.Assert;

+

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

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

+

+/**

+ * 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)}.

+     */

+    @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..9bc2fa6
--- /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.0.13</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..4131604
--- /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)}的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..2ab1105
--- /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.Adaptive;

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

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

+

+/**

+ * RegistryFactory. (SPI, Singleton, ThreadSafe)

+ * 

+ * NOTE: RegistryFactory should <strong>NOT</strong> have default implement.

+ * 

+ * @see com.alibaba.dubbo.registry.support.AbstractRegistryFactory

+ * @author william.liangf

+ */

+@Extension("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..5c8352a
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryService.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.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

+     */

+    void register(URL url);

+

+    /**

+     * 取消注册服务

+     * 

+     * @param url 服务提供者地址,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin

+     */

+    void unregister(URL url);

+

+    /**

+     * 订阅服务

+     * 

+     * 订阅需处理契约:<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..f56bbd9
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/RegisteredPageHandler.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.registry.pages;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+import java.util.Set;

+

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

+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

+ */

+@Extension("registered")

+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..b894832
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/RegistriesPageHandler.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.registry.pages;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+

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

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

+@Extension("registries")

+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..6feaa22
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/pages/SubscribedPageHandler.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.registry.pages;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+import java.util.Set;

+

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

+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

+ */

+@Extension("subscribed")

+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..1c54cf8
--- /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) {

+        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) {

+        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));

+            }

+        }

+        // 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));

+            } 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..cf78ba9
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryService.java
@@ -0,0 +1,214 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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) {

+        if (logger.isInfoEnabled()) {

+            logger.info("Register service: " + url.getServiceKey() + ",url:" + url);

+        }

+        register(url.getServiceKey(), url);

+    }

+

+    public void unregister(URL url) {

+        if (logger.isInfoEnabled()) {

+            logger.info("Unregister service: " + url.getServiceKey() + ",url:" + url);

+        }

+        unregister(url.getServiceKey(), url);

+    }

+

+    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) {

+        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);

+        }

+    }

+    

+    public void unregister(String service, URL url) {

+        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) {

+            urls.remove(url);

+        }

+    }

+    

+    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()); 

+        List<NotifyListener> listeners = notifyListeners.get(service);

+        if (listeners == null) {

+            notifyListeners.putIfAbsent(service, new CopyOnWriteArrayList<NotifyListener>());

+            listeners = notifyListeners.get(service);

+        }

+        listeners.add(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);

+        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..19fcffd
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
@@ -0,0 +1,372 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;

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

+

+/**

+ * FailbackRegistry

+ * 

+ * @author william.liangf

+ */

+public abstract class FailbackRegistry extends AbstractRegistry {

+

+    // 重试周期

+    private static final int DEFAULT_RETRY_PERIOD =  5 * 1000;

+    

+    // 定时任务执行器

+    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(RpcConstants.REGISTRY_RETRY_PERIOD_KEY, DEFAULT_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) {

+        super.register(url);

+        try {

+            // 向服务器端发送注册请求

+            doRegister(url);

+            removeFailedRegistered(url);

+        } catch (Exception t) {

+            if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常

+                throw new IllegalStateException("Failed to register " + url + ", 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) {

+        super.unregister(url);

+        try {

+            // 向服务器端发送取消注册请求

+            doUnregister(url);

+            removeFailedRegistered(url);

+        } catch (Exception t) {

+            if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常

+                throw new IllegalStateException("Failed to uregister " + url + ", 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 + ", 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 + ", 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) {

+            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..009e44a
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryDirectory.java
@@ -0,0 +1,463 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

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

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

+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.router.ScriptRouterFactory;

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

+

+/**

+ * 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 volatile boolean forbidden = false;

+    

+    private final String serviceKey;

+

+    private final Class<T> serviceType;

+

+    private volatile URL directoryUrl;

+

+    private volatile Map<String, String> queryMap;

+    

+    // 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(RpcConstants.REFER_KEY));

+        this.directoryUrl = url.removeParameters(RpcConstants.REFER_KEY, RpcConstants.EXPORT_KEY).addParameters(queryMap);

+    }

+

+    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>();

+            for (URL url : urls) {

+                if (RpcConstants.ROUTE_PROTOCOL.equals(url.getProtocol())) {

+                    if (! routerUrls.contains(url)) {

+                        routerUrls.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()));

+                }

+            }

+            

+            //route 

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

+                List<Router> routers = toRouters(routerUrls);

+                if(routers != null){ // null - do nothing

+                    setRouters(routers);

+                }

+            }

+            //invokers

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

+                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 :"+urls.toString()));

+                    return ;

+                }

+                this.methodInvokerMap = newMethodInvokerMap;

+                this.urlInvokerMap = newUrlInvokerMap;

+                try{

+                    destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker

+                }catch (Exception e) {

+                    logger.warn("destroyUnusedInvokers error. ", e);

+                }

+            }

+        }

+    }

+    

+    /**

+     * 

+     * @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(RpcConstants.ROUTER_TYPE_CLEAR.equals(u.getParameter(RpcConstants.ROUTER_KEY))){

+               return routers;

+           }

+        }

+        

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

+            for (URL url : urls) {

+                String router_type = url.getParameter(RpcConstants.ROUTER_KEY, ScriptRouterFactory.NAME);

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

+                    logger.warn("Router url:\"" + url.toString() + "\" does not contain " + RpcConstants.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

+     * 

+     * @param urls

+     * @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 url : urls) {

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

+                    if ((url.getPath() == null || url.getPath().length() == 0)

+                            && "dubbo".equals(url.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);

+                        }

+                        url = url.setPath(path);

+                    }

+                    url = ClusterUtils.mergeUrl(url, queryMap); // 合并消费端参数

+                    this.directoryUrl = this.directoryUrl.addParametersIfAbsent(url.getParameters()); // 合并提供者参数

+                    url = url.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // 不检查连接是否成功,总是创建Invoker!

+                    invoker = protocol.refer(serviceType, url);

+                } 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;

+    }

+

+    /**

+     * 将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 directoryUrl;

+    }

+

+    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());

+        }

+

+    }

+

+}
\ 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..30958a7
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryProtocol.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.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.Extension;

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

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

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

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

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

+import com.alibaba.dubbo.rpc.protocol.InvokerWrapper;

+

+/**

+ * RegistryProtocol

+ * 

+ * @author william.liangf

+ */

+@Extension(Constants.REGISTRY_PROTOCOL)

+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 final Map<String, Exporter<?>> bounds = new ConcurrentHashMap<String, Exporter<?>>();

+

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

+    public <T> Exporter<T> export(final Invoker<T> invoker) throws RpcException {

+        String export = invoker.getUrl().getParameterAndDecoded(RpcConstants.EXPORT_KEY);

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

+            throw new IllegalArgumentException("The registry export url is null! registry: " + invoker.getUrl());

+        }

+        

+        URL url = URL.valueOf(export);

+        final String key = url.removeParameters("dynamic", "enabled").toFullString();

+        Exporter<T> exporter = (Exporter) bounds.get(key);

+        if (exporter == null) {

+            synchronized (bounds) {

+                exporter = (Exporter) bounds.get(key);

+                if (exporter == null) {

+                    exporter = protocol.export(new InvokerWrapper<T>(invoker, url));

+                    bounds.put(key, exporter);

+                }

+            }

+        }

+        

+        URL registryUrl = invoker.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);

+        }

+        final Exporter<T> serviceExporter = exporter;

+        final URL serviceUrl = url.removeParameters(getFilteredKeys(url));

+        final Registry registry = registryFactory.getRegistry(registryUrl);

+        registry.register(serviceUrl);

+        

+        return new Exporter<T>() {

+            public Invoker<T> getInvoker() {

+                return invoker;

+            }

+            public void unexport() {

+                bounds.remove(key);

+                try {

+                    registry.unregister(serviceUrl);

+                } finally {

+                    serviceExporter.unexport();

+                }

+            }

+        };

+    }

+

+    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);

+        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.merge(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() {

+    }

+

+}
\ 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..b3577b8
--- /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;

+import com.alibaba.dubbo.common.status.Status;

+import com.alibaba.dubbo.common.status.StatusChecker;

+import com.alibaba.dubbo.registry.Registry;

+

+/**

+ * RegistryStatusChecker

+ * 

+ * @author william.liangf

+ */

+@Extension("registry")

+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..b806b81
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryExporter.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.registry.support;

+

+import java.io.IOException;

+import java.net.ServerSocket;

+

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

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

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

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

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

+

+/**

+ * 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(RpcConstants.CLUSTER_STICKY_KEY, "true")

+                .addParameter(RpcConstants.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..5dc3faf
--- /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) {

+        super.register(service, url);

+        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) {

+        super.unregister(service, url);

+        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()));

+            List<String> rs = registries;

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

+                for (String registry : rs) {

+                    register(service, UrlUtils.parseURL(registry, url.getParameters()));

+                }

+            }

+        }

+        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());

+            }

+        }

+        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/services/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..58920c6
--- /dev/null
+++ b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1 @@
+com.alibaba.dubbo.registry.support.RegistryStatusChecker
\ No newline at end of file
diff --git a/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..5ac6135
--- /dev/null
+++ b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.registry.pages.RegistriesPageHandler

+com.alibaba.dubbo.registry.pages.RegisteredPageHandler

+com.alibaba.dubbo.registry.pages.SubscribedPageHandler
\ No newline at end of file
diff --git a/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..c210ed7
--- /dev/null
+++ b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+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..2c68266
--- /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) {

+                }

+

+                public void unregister(URL url) {

+                }

+

+                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..832aaa5
--- /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.ExtensionLoader;

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

+

+/**

+ * 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)));

+                    }

+                }

+            }).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..90a6d5b
--- /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.0.13</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..1a3e37f
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.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.grizzly;

+

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

+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

+ */

+@Extension(GrizzlyTransporter.NAME)

+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/services/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting-grizzly/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..5ce7f67
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+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..df68ed8
--- /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.0.13</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..a7356bb
--- /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.Adaptive;

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

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

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

+

+/**

+ * HttpBinder

+ * 

+ * @author william.liangf

+ */

+@Extension("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..dc67f11
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.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.jetty;

+

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

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

+import com.alibaba.dubbo.remoting.http.HttpHandler;

+import com.alibaba.dubbo.remoting.http.HttpServer;

+import com.alibaba.dubbo.remoting.http.HttpBinder;

+

+/**

+ * JettyHttpTransporter

+ * 

+ * @author william.liangf

+ */

+@Extension("jetty")

+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..ef459ee
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.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.http.servlet;

+

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

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

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

+

+/**

+ * ServletHttpTransporter

+ * 

+ * @author william.liangf

+ */

+@Extension("servlet")

+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/services/com.alibaba.dubbo.remoting.http.HttpBinder b/dubbo-remoting-http/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.http.HttpBinder
new file mode 100644
index 0000000..46f65c4
--- /dev/null
+++ b/dubbo-remoting-http/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.http.HttpBinder
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.remoting.http.servlet.ServletHttpBinder

+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..e526836
--- /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.0.13</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..86bbafe
--- /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.handler.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..6b7c18e
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.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.mina;
+
+import com.alibaba.dubbo.common.Extension;
+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
+ */
+@Extension(MinaTransporter.NAME)
+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/services/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting-mina/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..aaaf989
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+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..ed7e059
--- /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.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..be5216b
--- /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.0.13</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..e24e251
--- /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.handler.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..787d73f
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.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.netty;
+
+import com.alibaba.dubbo.common.Extension;
+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
+ */
+@Extension(NettyTransporter.NAME)
+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/services/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting-netty/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..d90141b
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+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..0493ed8
--- /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.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..ecd8695
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.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.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 {
+        /*int port = (int) (1000 * Math.random() + 10000);
+        //System.out.println(port);*/
+        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());
+            System.out.println(i);
+            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..0e42c58
--- /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.0.13</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..c7fafe1
--- /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.Extension;

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

+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

+ */

+@Extension

+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..46c1ce6
--- /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.ExtensionLoader;

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

+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..8627608
--- /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.ExtensionLoader;

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

+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..a5835ca
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.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.support;

+

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

+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

+ */

+@Extension("file")

+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..3cd808e
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.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.support;

+

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

+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

+ */

+@Extension("multicast")

+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..6998a4a
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.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.support;

+

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

+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

+ */

+@Extension("file")

+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..caf2b3f
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.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.support;

+

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

+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

+ */

+@Extension("multicast")

+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/services/com.alibaba.dubbo.remoting.p2p.Networker b/dubbo-remoting-p2p/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.p2p.Networker
new file mode 100644
index 0000000..cd1e3fc
--- /dev/null
+++ b/dubbo-remoting-p2p/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.p2p.Networker
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.remoting.p2p.support.MulticastNetworker

+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..048d201
--- /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.0.13</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..1b0d814
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.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;
+
+
+/**
+ * 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
+ */
+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/ChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandlerWrapper.java
new file mode 100644
index 0000000..42fd17c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandlerWrapper.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.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.transport.handler.DefaultChannelHandlerWrapper;
+
+/**
+ * ChannelHandlerWrapper (SPI, Singleton, ThreadSafe)
+ * 
+ * @author chao.liuc
+ */
+@Extension(DefaultChannelHandlerWrapper.NAME)
+public interface ChannelHandlerWrapper {
+    
+    /**
+     * wrap.
+     * 
+     * @param handler
+     * @param url
+     * @return channel handler
+     */
+    @Adaptive({Constants.CHANNEL_HANDLER_KEY})
+    ChannelHandler wrap(ChannelHandler handler, URL url);
+    
+}
\ 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..3157650
--- /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.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;

+
+/**
+ * Codec. (SPI, Singleton, ThreadSafe)
+ * 
+ * @author qianlei
+ * @author ding.lid
+ * @author william.liangf
+ */

+@Extension
+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/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..fd37fad
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporter.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 com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * 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
+ */
+@Extension("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..0c56d05
--- /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.ExtensionLoader;

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

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

+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..10ab3ed
--- /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.Adaptive;

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

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

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

+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

+ */

+@Extension(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..f2cd282
--- /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.ExtensionLoader;

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

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

+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..2477bed
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java
@@ -0,0 +1,435 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;

+import com.alibaba.dubbo.common.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
+ */
+@Extension("exchange")
+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..c942282
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.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.exchange.support.header;

+

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

+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 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);

+    }

+

+    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() {

+        channel.close();

+    }

+

+    public void close(int timeout) {

+        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);

+    }

+

+}
\ 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..e9621d3
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.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.remoting.exchange.support.header;

+

+import java.net.InetSocketAddress;

+import java.util.ArrayList;

+import java.util.Collection;

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

+        try {

+            if (null != heatbeatTimer) {

+                heatbeatTimer.cancel(true);

+                heatbeatTimer = null;

+            }

+        } catch (Throwable t) {

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

+        }

+        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() {

+        try {

+            ScheduledFuture<?> timer = heatbeatTimer;

+            if (timer != null && ! timer.isCancelled()) {

+                timer.cancel(true);

+            }

+        } catch (Throwable t) {

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

+        }

+        if (heartbeat > 0) {

+            heatbeatTimer = scheduled.scheduleWithFixedDelay(new HeartBeatTask(), heartbeat, heartbeat,

+                                                             TimeUnit.MILLISECONDS);

+        }

+    }

+

+    private class HeartBeatTask implements Runnable {

+        public void run() {

+            try {

+                long now = System.currentTimeMillis();

+                for (Channel channel : getChannels()) {

+                    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 client " + channel.getRemoteAddress() + ".");

+                            }

+                        }

+                        if (lastRead != null && now - lastRead > heartbeatTimeout) {

+                            logger.warn("Close remote client " + channel.getRemoteAddress()

+                                        + ", because heartbeat read idle time out.");

+                            channel.close();

+                        }

+                    } catch (Throwable t) {

+                        logger.warn("Exception when heartbeat to client " + (InetSocketAddress) channel.getRemoteAddress(), t);

+                    }

+                }

+            } catch (Throwable t) {

+                logger.info("Exception when heartbeat to clients: ", t);

+            }

+        }

+    }

+

+}
\ 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..c1506ad
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.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.support.header;

+

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

+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

+ */

+@Extension(HeaderExchanger.NAME)

+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/telnet/TelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java
new file mode 100644
index 0000000..cca6c56
--- /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;

+import com.alibaba.dubbo.remoting.Channel;

+import com.alibaba.dubbo.remoting.RemotingException;

+

+/**

+ * TelnetHandler

+ * 

+ * @author william.liangf

+ */

+@Extension

+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..baba9ad
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java
@@ -0,0 +1,309 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Extension;

+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
+ */
+@Extension("telnet")
+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..8a592ae
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.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.remoting.telnet.support;

+

+import java.util.List;

+

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

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

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

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

+    

+    public String telnet(Channel channel, String message) throws RemotingException {

+        String telnet = channel.getUrl().getParameter("telnet");

+        String prompt = channel.getUrl().getParameter("prompt");

+        boolean noprompt = message.contains("--no-prompt");

+        message = message.replace("--no-prompt", "");

+        List<String> commands = ConfigUtils.mergeValues(TelnetHandler.class, telnet, Constants.DEFAULT_TELNET_COMMANDS);

+        StringBuilder buf = new StringBuilder();

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

+            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 (commands.contains(command)) {

+                TelnetHandler handler = ExtensionLoader.getExtensionLoader(TelnetHandler.class).getExtension(command);

+                try {

+                    String result = handler.telnet(channel, message);

+                    if (result != null) {

+                        buf.append(result);

+                    }

+                } catch (Throwable t) {

+                    buf.append(t.getMessage());

+                }

+            } else if (command.length() > 0) {

+                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);

+            buf.append(">");

+        }

+        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..a87d050
--- /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;

+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

+ */

+@Help(parameter = "[lines]", summary = "Clear screen.", detail = "Clear screen.")

+@Extension("clear")

+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..ce933e3
--- /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;

+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

+ */

+@Help(parameter = "", summary = "Exit the telnet.", detail = "Exit the telnet.")

+@Extension("exit")

+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..236fe94
--- /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.Constants;

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

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

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

+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

+ */

+@Help(parameter = "[command]", summary = "Show help.", detail = "Show help.")

+@Extension("help")

+public class HelpTelnetHandler implements TelnetHandler {

+

+    public String telnet(Channel channel, String message) {

+        if (message.length() > 0) {

+            if (! ExtensionLoader.getExtensionLoader(TelnetHandler.class).hasExtension(message)) {

+                return "No such command " + message;

+            }

+            TelnetHandler handler = ExtensionLoader.getExtensionLoader(TelnetHandler.class).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>>();

+            String telnet = channel.getUrl().getParameter("telnet");

+            List<String> cmds = ConfigUtils.mergeValues(TelnetHandler.class, telnet, Constants.DEFAULT_TELNET_COMMANDS);

+            for (String cmd : cmds) {

+                TelnetHandler handler = ExtensionLoader.getExtensionLoader(TelnetHandler.class).getExtension(cmd);

+                Help help = handler.getClass().getAnnotation(Help.class);

+                List<String> row = new ArrayList<String>();

+                String parameter = " " + cmd + " " + (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/StatusTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
new file mode 100644
index 0000000..164db61
--- /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;

+import com.alibaba.dubbo.common.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.common.utils.ConfigUtils;

+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

+ */

+@Help(parameter = "[-l]", summary = "Show status.", detail = "Show status.")

+@Extension("status")

+public class StatusTelnetHandler implements TelnetHandler {

+

+    public String telnet(Channel channel, String message) {

+        if (message.equals("-l")) {

+            String status = channel.getUrl().getParameter("status");

+            List<String> ss = ConfigUtils.mergeValues(StatusChecker.class, status, Constants.DEFAULT_CHECK_STATUSES);

+            String[] header = new String[] {"resource", "status", "message"};

+            List<List<String>> table = new ArrayList<List<String>>();

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

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

+                for (String s : ss) {

+                    StatusChecker handler = ExtensionLoader.getExtensionLoader(StatusChecker.class).getExtension(s);

+                    Status stat;

+                    try {

+                        stat = handler.check();

+                    } catch (Throwable t) {

+                        stat = new Status(Status.Level.ERROR, t.getMessage());

+                    }

+                    statuses.put(s, stat);

+                    if (stat.getLevel() != null && stat.getLevel() != Status.Level.UNKNOWN) {

+                        List<String> row = new ArrayList<String>();

+                        row.add(s);

+                        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.getExtensionLoader(StatusChecker.class).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..ecdd9c2
--- /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.handler.ChannelHandlers;

+import com.alibaba.dubbo.remoting.transport.handler.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..a4c8895
--- /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.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..a220a74
--- /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.ExtensionLoader;

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

+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.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..b135567
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.java
@@ -0,0 +1,210 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

+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.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.handler.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() {

+        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..3ac299d
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.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.remoting.transport.codec;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+

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

+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

+ */

+@Extension("transport")

+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/handler/ChannelEventRunnable.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ChannelEventRunnable.java
new file mode 100644
index 0000000..58d7270
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/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.handler;
+
+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/handler/ChannelHandlers.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ChannelHandlers.java
new file mode 100644
index 0000000..931a1c0
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/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.handler;
+
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class ChannelHandlers {
+
+    public static ChannelHandler wrap(ChannelHandler handler, URL url){
+        return ExtensionLoader.getExtensionLoader(ChannelHandlerWrapper.class)
+            .getAdaptiveExtension().wrap(handler, url);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandler.java
new file mode 100644
index 0000000..ae80764
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandler.java
@@ -0,0 +1,104 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.handler;
+
+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.handler.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/handler/ConnectionOrderedChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandlerWrapper.java
new file mode 100644
index 0000000..48c5bef
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandlerWrapper.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.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * connect disconnect 保证顺序.
+ * 
+ * @author chao.liuc
+ */
+@Extension(ConnectionOrderedChannelHandlerWrapper.NAME)
+public class ConnectionOrderedChannelHandlerWrapper implements ChannelHandlerWrapper {
+
+    public static final String NAME = "connection";
+
+    public ChannelHandler wrap(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/handler/DefaultChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandler.java
new file mode 100644
index 0000000..6064e26
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandler.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.remoting.transport.handler;
+
+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.handler.ChannelEventRunnable.ChannelState;

+
+public class DefaultChannelHandler extends WrappedChannelHandler {
+    
+    public DefaultChannelHandler(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/handler/DefaultChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandlerWrapper.java
new file mode 100644
index 0000000..5d5da8c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandlerWrapper.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.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * 默认的线程池配置
+ * 
+ * @author chao.liuc
+ */
+@Extension(DefaultChannelHandlerWrapper.NAME)
+public class DefaultChannelHandlerWrapper implements ChannelHandlerWrapper {
+    
+    public static final String NAME = "default";
+
+    public ChannelHandler wrap(ChannelHandler handler, URL url) {
+        return new DefaultChannelHandler(handler, url);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DirectChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DirectChannelHandlerWrapper.java
new file mode 100644
index 0000000..011307f
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DirectChannelHandlerWrapper.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.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * 不派发线程池。
+ * 
+ * @author chao.liuc
+ */
+@Extension(DirectChannelHandlerWrapper.NAME)
+public class DirectChannelHandlerWrapper implements ChannelHandlerWrapper {
+    
+    public static final String NAME = "direct";
+
+    public ChannelHandler wrap(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/handler/ExecutionChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandler.java
new file mode 100644
index 0000000..3a4e119
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandler.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.remoting.transport.handler;
+
+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.handler.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/handler/ExecutionChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandlerWrapper.java
new file mode 100644
index 0000000..7877471
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandlerWrapper.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.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * 除发送全部使用线程池处理
+ * 
+ * @author chao.liuc
+ */
+@Extension(ExecutionChannelHandlerWrapper.NAME)
+public class ExecutionChannelHandlerWrapper implements ChannelHandlerWrapper {
+    
+    public static final String NAME = "execution";
+
+    public ChannelHandler wrap(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/handler/MessageOnlyChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandler.java
new file mode 100644
index 0000000..b9ebbe9
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandler.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.remoting.transport.handler;
+
+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.handler.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/handler/MessageOnlyChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandlerWrapper.java
new file mode 100644
index 0000000..a6afc89
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandlerWrapper.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.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * 只有message receive使用线程池.
+ * 
+ * @author chao.liuc
+ */
+@Extension(MessageOnlyChannelHandlerWrapper.NAME)
+public class MessageOnlyChannelHandlerWrapper implements ChannelHandlerWrapper {
+
+    public static final String NAME = "message";
+
+    public ChannelHandler wrap(ChannelHandler handler, URL url) {
+        return new MessageOnlyChannelHandler(handler, url);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/WrappedChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/WrappedChannelHandler.java
new file mode 100644
index 0000000..b1c43f1
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/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.handler;
+
+import java.util.concurrent.ExecutorService;

+import java.util.concurrent.Executors;

+

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

+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.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/resources/META-INF/services/com.alibaba.dubbo.remoting.ChannelHandlerWrapper b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.ChannelHandlerWrapper
new file mode 100644
index 0000000..bf00c1b
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.ChannelHandlerWrapper
@@ -0,0 +1,5 @@
+com.alibaba.dubbo.remoting.transport.handler.DefaultChannelHandlerWrapper

+com.alibaba.dubbo.remoting.transport.handler.DirectChannelHandlerWrapper

+com.alibaba.dubbo.remoting.transport.handler.ExecutionChannelHandlerWrapper

+com.alibaba.dubbo.remoting.transport.handler.MessageOnlyChannelHandlerWrapper

+com.alibaba.dubbo.remoting.transport.handler.ConnectionOrderedChannelHandlerWrapper
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec
new file mode 100644
index 0000000..bed639d
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.remoting.transport.codec.TransportCodec

+com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec

+com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.exchange.Exchanger b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.exchange.Exchanger
new file mode 100644
index 0000000..6084189
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.exchange.Exchanger
@@ -0,0 +1 @@
+com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.p2p.Networker b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.p2p.Networker
new file mode 100644
index 0000000..cd1e3fc
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.p2p.Networker
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.remoting.p2p.support.MulticastNetworker

+com.alibaba.dubbo.remoting.p2p.support.FileNetworker
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler
new file mode 100644
index 0000000..cc16268
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.remoting.telnet.support.command.ClearTelnetHandler

+com.alibaba.dubbo.remoting.telnet.support.command.ExitTelnetHandler

+com.alibaba.dubbo.remoting.telnet.support.command.HelpTelnetHandler

+com.alibaba.dubbo.remoting.telnet.support.command.StatusTelnetHandler
\ 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..ab32827
--- /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.handler.ExecutionChannelHandlerWrapper;
+
+/**
+ * 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, ExecutionChannelHandlerWrapper.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..30de347
--- /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.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/handler/ConnectChannelHandlerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.java
new file mode 100644
index 0000000..51a8674
--- /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.handler.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..eb090e6
--- /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.handler.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..2faaeb7
--- /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.0.13</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..67feced
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
@@ -0,0 +1,287 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ExtensionLoader;

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

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

+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(RpcConstants.IS_SERVER_KEY, Boolean.FALSE.toString());
+        //标识callback 变于排查问题
+        params.put(RpcConstants.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(RpcConstants.CHANNEL_CALLBACK_KEY);
+                    if (callbackInvokers == null){
+                        callbackInvokers = new ConcurrentHashSet<Invoker<?>>(1);
+                        callbackInvokers.add(invoker);
+                        channel.setAttribute(RpcConstants.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(RpcConstants.CHANNEL_CALLBACK_KEY);
+                    if (callbackInvokers != null ) {
+                        callbackInvokers.remove(invoker);
+                    }
+                    invoker.destroy();
+                }catch (Exception e) {
+                    logger.error(e);
+                }
+                //取消refer 直接在map中去除,
+                channel.removeAttribute(proxyCacheKey);
+                channel.removeAttribute(invokerCacheKey);
+                decreaseInstanceCount(channel,countkey);
+            }
+        } 
+        return proxy;
+    }
+    
+    private static String getClientSideCallbackServiceCacheKey(int instid){
+        return RpcConstants.CALLBACK_SERVICE_KEY+"."+instid;
+    }
+    private static String getServerSideCallbackServiceCacheKey(Channel channel, String interfaceClass, int instid){
+        return RpcConstants.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 RpcConstants.CALLBACK_SERVICE_KEY+"."+interfaceClass+".COUNT";
+    }
+    private static String getServerSideCountKey(Channel channel, String interfaceClass){
+        return RpcConstants.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(RpcConstants.CALLBACK_INSTANCES_LIMIT_KEY, RpcConstants.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);
+        }
+    }
+    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);
+        }
+    }
+    
+    public static Object encodeInvocationArgument(Channel channel, RpcInvocation inv, int paraIndex) throws IOException{

+        //encode时可直接获取url

+        URL url = inv.getUrl();
+        byte callbackstatus = isCallBack(channel == null ? null : 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) {

+            logger.error("get invoker error", 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);
+                    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..b2ec531
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.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.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.RpcConstants;
+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(RpcConstants.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..ef248ba
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.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.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.Extension;

+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
+ */
+@Extension(value=DubboCodec.NAME)
+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.getResult();
+            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.setResult(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..ebe0224
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.java
@@ -0,0 +1,145 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.Result;

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

+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();

+    

+    public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients){

+        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");

+    }

+

+    @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, RpcConstants.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();

+                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..b85f65b
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.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.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.Extension;

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

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

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

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

+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
+ */
+@Extension(DubboProtocol.NAME)
+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, RpcConstants.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, RpcConstants.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(RpcConstants.STUB_EVENT_KEY, false)){
+                invocation.setAttachment(RpcConstants.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(RpcConstants.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(RpcConstants.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);
+        // find server.
+        String key = url.getAddress();
+        //client 也可以暴露一个只有server可以调用的服务。
+        boolean isServer = url.getParameter(RpcConstants.IS_SERVER_KEY,true);
+        if (isServer && ! serverMap.containsKey(key)) {

+            serverMap.put(key, getServer(url));
+        }
+        // export service.
+        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(RpcConstants.STUB_EVENT_KEY,RpcConstants.DEFAULT_STUB_EVENT);
+        Boolean isCallbackservice = url.getParameter(RpcConstants.IS_CALLBACK_SERVICE, false);
+        if (isStubSupportEvent && !isCallbackservice){
+            String stubServiceMethods = url.getParameter(RpcConstants.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);
+            }
+        }
+        return exporter;
+    }
+    
+    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.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(RpcConstants.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..1d02395
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java
@@ -0,0 +1,221 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;

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

+
+/**
+ * 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(RpcConstants.LAZY_CONNECT_INITIAL_STATE_KEY,RpcConstants.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..350d506
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
@@ -0,0 +1,173 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;

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

+
+/**
+ * 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(RpcConstants.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..8de2140
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.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.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;

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

+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

+ */

+@Extension("future")

+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.getResult());

+        }

+    }

+    

+    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.getResult());

+                    }

+                }

+                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(), RpcConstants.ON_INVOKE_METHOD_KEY));

+        final Object onInvokeInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), RpcConstants.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(), RpcConstants.ON_RETURN_METHOD_KEY));

+        final Object onReturnInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), RpcConstants.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(), RpcConstants.ON_THROW_METHOD_KEY));

+        final Object onthrowInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), RpcConstants.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..66f47be
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.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.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.Extension;

+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

+ */

+@Extension("trace")

+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("prompt", "telnet");

+                                channel.send("\r\n" + RpcContext.getContext().getRemoteAddress() + " -> "  

+                                         + invoker.getInterface().getName() 

+                                         + "." + invocation.getMethodName() 

+                                         + "(" + JSON.json(invocation.getArguments()) + ")" + " -> " + JSON.json(result.getResult())

+                                         + "\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..ff64e6c
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ClientsPageHandler.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.protocol.dubbo.page;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+

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

+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

+ */

+@Extension("clients")

+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..651d546
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ServersPageHandler.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.protocol.dubbo.page;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+

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

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

+@Extension("servers")

+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..b69ad3f
--- /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;

+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

+ */

+@Extension("server")

+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..7c80643
--- /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;

+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.handler.WrappedChannelHandler;

+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;

+

+/**

+ * ThreadPoolStatusChecker

+ * 

+ * @author william.liangf

+ */

+@Extension("threadpool")

+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..0394f2c
--- /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;

+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

+ */

+@Help(parameter = "[service]", summary = "Change default service.", detail = "Change default service.")

+@Extension("cd")

+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..27d8a46
--- /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.Extension;

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

+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

+ */

+@Help(parameter = "[service] [method] [times]", summary = "Count the service.", detail = "Count the service.")

+@Extension("count")

+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..b81b82c
--- /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;

+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

+ */

+@Help(parameter = "", summary = "Print working default service.", detail = "Print working default service.")

+@Extension("pwd")

+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..1d3ca1d
--- /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;

+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

+ */

+@Help(parameter = "[service.]method(args)", summary = "Invoke the service method.", detail = "Invoke the service method.")

+@Extension("invoke")

+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..28c0ada
--- /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;

+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

+ */

+@Help(parameter = "[-l] [service]", summary = "List services and methods.", detail = "List services and methods.")

+@Extension("ls")

+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..37b273d
--- /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;
+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
+ * 
+ */
+@Help(parameter = "level", summary = "Change log level or show log ", detail = "Change log level or show log")
+@Extension("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..0743394
--- /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;

+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

+ */

+@Help(parameter = "[-l] [port]", summary = "Print server ports and connections.", detail = "Print server ports and connections.")

+@Extension("ps")

+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..af6a291
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.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.rpc.protocol.dubbo.telnet;

+

+import java.lang.reflect.Method;

+

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

+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

+ */

+@Help(parameter = "[service] [method] [times]", summary = "Trace the service.", detail = "Trace the service.")

+@Extension("trace")

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

+        }

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

+        }

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

+            buf.append("No such service " + service);

+        }

+        return buf.toString();

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..0dd9b93
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.status.ServerStatusChecker

+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/services/com.alibaba.dubbo.container.page.PageHandler b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..e3245bc
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.page.ServersPageHandler

+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/services/com.alibaba.dubbo.rpc.Filter b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..89488e5
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter

+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/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..b415e0f
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+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..4b63b5f
--- /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.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+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..4179cf0
--- /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.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.RpcConstants;
+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?"+RpcConstants.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?"+RpcConstants.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?"+RpcConstants.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..684097b
--- /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.ExtensionLoader;

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

+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..76c85f2
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java
@@ -0,0 +1,360 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.RpcConstants;
+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"
+                +"&"+RpcConstants.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(RpcConstants.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..8ecfefd
--- /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.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.RpcConstants;

+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.setResult("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.getResult());

+    }

+

+    @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&"+RpcConstants.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..12d8bdc
--- /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.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.RpcConstants;

+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", RpcConstants.ON_THROW_METHOD_KEY),onThrowMethod);
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_THROW_INSTANCE_KEY),notify);
+    }
+    public void initImplicitCallBackURL_onlyOnreturn() throws Exception {
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_RETURN_METHOD_KEY),onReturnMethod);
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_RETURN_INSTANCE_KEY),notify);
+        
+    }
+    public void initImplicitCallBackURL_onlyOninvoke() throws Exception {
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_INVOKE_METHOD_KEY),onInvokeMethod);
+        StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.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..7fd9c0f
--- /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.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+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..f9f51e2
--- /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.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+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..b89564b
--- /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.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+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..fd565f2
--- /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.ExtensionLoader;

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

+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..141d64a
--- /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.ExtensionLoader;

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

+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..24a733a
--- /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.0.13</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..b5b53b4
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.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.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.Extension;

+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
+ */
+@Extension("hessian")
+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/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-hessian/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..9b80f39
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+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..d3b449e
--- /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.ExtensionLoader;

+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.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..c84a5be
--- /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.0.13</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..a3e8139
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.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.protocol.injvm;
+
+import com.alibaba.dubbo.common.Extension;

+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
+ */
+@Extension(InjvmProtocol.NAME)
+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/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-injvm/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..2b9dc61
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+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..2905069
--- /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.ExtensionLoader;

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

+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..2adaced
--- /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.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+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..d5fdd51
--- /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.0.13</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..284228c
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.java
@@ -0,0 +1,304 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Extension;

+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

+ */

+@Extension("rmi")

+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..740bf80
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProxyFactory.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.protocol.rmi;

+

+import java.rmi.Remote;

+

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

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

+

+/**

+ * RmiProxyFactory

+ * 

+ * @author william.liangf

+ */

+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..f373a01
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/DubboRmiProxyFactory.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.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.Extension;

+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

+ */

+@Extension("dubbo")

+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..9150298
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/NativeProxyFactory.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.protocol.rmi.proxy;

+

+import java.rmi.Remote;

+import java.rmi.server.RemoteServer;

+import java.rmi.server.ServerNotActiveException;

+

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

+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

+ */

+@Extension("native")

+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..37ce73b
--- /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.Adaptive;

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

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

+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..0f6556b
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/SpringRmiProxyFactory.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.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.Extension;

+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

+ */

+@Extension("spring")

+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.setResult(((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/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..62809e8
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory b/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory
new file mode 100644
index 0000000..2c2b9fc
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.rpc.protocol.rmi.proxy.RmiProxyFactoryAdaptive

+com.alibaba.dubbo.rpc.protocol.rmi.proxy.NativeProxyFactory

+com.alibaba.dubbo.rpc.protocol.rmi.proxy.DubboRmiProxyFactory

+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..617348e
--- /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.ExtensionLoader;

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

+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..9eff76f
--- /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.0.13</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..0de6536
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.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;

+

+/**

+ * ExporterListener. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+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..2a0573f
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Filter.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;
+

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

+

+/**
+ * Filter. (SPI, Singleton, ThreadSafe)
+ * 
+ * @author william.liangf
+ */

+@Extension
+public interface Filter {
+
+	/**
+	 * do invoke filter.
+	 * 
+	 * <code>
+     *     return invoker.invoke(invocation);
+     * </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..9915f2b
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invocation.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;
+
+import java.util.Map;
+

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

+
+/**
+ * Rpc invocation. (API, Prototype, ThreadSafe)
+ * 
+ * @serial Don't change the class 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 the url.

+     * 

+     * @return url.

+     */

+    URL getUrl();
+    
+	/**
+	 * get method name.
+	 * 
+	 * @return method name.
+	 */
+	String getMethodName();
+
+	/**
+	 * get parameter types.
+	 * 
+	 * @return parameter types.
+	 */
+	Class<?>[] getParameterTypes();
+
+	/**
+	 * get arguments.
+	 * 
+	 * @return arguments.
+	 */
+	Object[] getArguments();
+
+	/**
+	 * get attachments.
+	 * 
+	 * @return attachments.
+	 */
+	Map<String, String> getAttachments();
+
+}
\ 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..f55b223
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invoker.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;

+

+/**

+ * Invoker. (API/SPI, Prototype, ThreadSafe)

+ * 

+ * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, URL)

+ * @see com.alibaba.dubbo.rpc.InvokerListener

+ * @see com.alibaba.dubbo.rpc.protocol.AbstractInvoker

+ * @author william.liangf

+ */

+public interface Invoker<T> {

+

+    /**

+     * get service interface.

+     * 

+     * @return service interface.

+     */

+    Class<T> getInterface();

+

+    /**

+     * get service url.

+     * 

+     * @return service url.

+     */

+    URL getUrl();

+    

+    /**

+     * is available.

+     * 

+     * @return available.

+     */

+    boolean isAvailable();

+

+    /**

+     * invoke.

+     * 

+     * @param invocation

+     * @return result

+     * @throws RpcException

+     */

+    Result invoke(Invocation invocation) throws RpcException;

+

+    /**

+     * destroy.

+     */

+    void destroy();

+

+}
\ 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..2be31ce
--- /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.Extension;

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

+

+/**

+ * InvokerListener. (SPI, Singleton, ThreadSafe)

+ * 

+ * @author william.liangf

+ */

+@Extension

+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..2158bd3
--- /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.Adaptive;
+import com.alibaba.dubbo.common.Extension;

+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Protocol. (SPI, Singleton, ThreadSafe)
+ * 
+ * @author william.liangf
+ */

+@Extension("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..edcdc62
--- /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.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * ProxyFactory. (SPI, Singleton, ThreadSafe)
+ * 
+ * @author william.liangf
+ */
+@Extension("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..57402cd
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Result.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;
+
+/**
+ * RPC invoke result. (API, Prototype, ThreadSafe)
+ * 
+ * @serial Don't change the class name.
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @see com.alibaba.dubbo.rpc.RpcResult
+ * @author qianlei
+ * @author william.liangf
+ */
+public interface Result {
+
+    /**
+     * Has exception.
+     * 
+     * @return has exception.
+     */
+    boolean hasException();
+
+	/**
+	 * Get invoke result.
+	 * 
+	 * @return result if has exception throw it.
+	 * @throws Throwable.
+	 */
+	Object getResult();
+
+	/**
+	 * Get exception.
+	 * 
+	 * @return exception if no exception return null.
+	 */
+	Throwable getException();
+
+    /**
+     * Recreate.
+     * 
+     * <code>
+     * if (hasException()) {
+     *     throw getException();
+     * } else {
+     *     return getResult();
+     * }
+     * </code>
+     */
+    Object recreate() throws Throwable;
+
+}
\ 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..cf58fa1
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java
@@ -0,0 +1,175 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Arrays;

+import java.util.Collections;

+import java.util.List;

+

+/**

+ * RpcConstants

+ * 

+ * @author william.liangf

+ */

+public final class RpcConstants {

+    

+    public static final List<String> DEFAULT_REFERENCE_FILTERS = Collections.unmodifiableList(Arrays.asList(new String[] {

+            "consumercontext", "deprecated", "collect", "genericimpl", "activelimit", "monitor", "future" }));

+

+    public static final List<String> DEFAULT_SERVICE_FILTERS    = Collections.unmodifiableList(Arrays.asList(new String[] {

+            "context", "token", "exception", "echo", "generic", "accesslog", "trace", "classloader", "executelimit", "monitor" ,"timeout"}));

+

+    public static final List<String> DEFAULT_INVOKER_LISTENERS = Collections.unmodifiableList(Arrays.asList(new String[] {

+            "deprecated" }));

+

+    public static final List<String> DEFAULT_EXPORTER_LISTENERS = Collections.unmodifiableList(Arrays.asList(new String[] { }));

+

+    /**

+     * 集群时是否排除非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 String REGISTRY_RECONNECT_PERIOD_KEY = "reconnect.period";

+    

+    /**

+     * 注册中心导出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_TIMEOUT_SECONDS_KEY = "dubbo.service.shutdown.wait.seconds";

+    

+    public static final String SHUTDOWN_TIMEOUT_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 ROUTE_PROTOCOL = "route";

+    

+    public static final String RULE_KEY = "rule";

+

+    public static final String TYPE_KEY = "type";

+    

+    // key for router type, for e.g., "script"/"file",  corresponding to ScriptRouterFactory.NAME, FileRouterFactory.NAME 

+    public static final String ROUTER_KEY = "router";

+

+    // 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";

+    

+    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..124bb0d
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcException.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;
+
+/**
+ * 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 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;
+
+    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..e06d9fe
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.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;
+
+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.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 URL       url;

+
+    public RpcInvocation() {
+    }

+    

+    public RpcInvocation(Invocation invocation) {

+        this(invocation.getMethodName(), invocation.getParameterTypes(), 

+                invocation.getArguments(), invocation.getAttachments(), invocation.getUrl());

+    }
+
+    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, URL url) {

+        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.url = url;

+    }

+    

+    public URL getUrl() {

+        return url;

+    }

+

+    public void setUrl(URL url) {

+        this.url = url;

+    }

+    
+    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..a732565
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcResult.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.rpc;
+
+import java.io.Serializable;

+

+/**
+ * Generic rpc invoke 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;
+    }
+
+    public Object getResult() {
+        return result;
+    }
+
+    public void setResult(Object result) {
+        this.result = result;
+    }
+
+    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..16086f2
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java
@@ -0,0 +1,309 @@
+/*

+ * Copyright 1999-2011 Alibaba Group.

+ *  

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *  

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *  

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF 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();

+    }

+

+}
\ 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..a6ea174
--- /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;
+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.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;
+
+/**
+ * 记录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
+ */
+@Extension("accesslog")
+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..f3c86bd
--- /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.Extension;

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

+

+/**

+ * LimitInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Extension("activelimit")

+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..f5b9a90
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.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.filter;

+

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

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

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

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

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

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

+

+/**

+ * ClassLoaderInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Extension("classloader")

+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..278591f
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.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.rpc.filter;

+

+import java.lang.reflect.Method;

+import java.lang.reflect.Type;

+

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

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

+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

+ */

+@Extension("compatible")

+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.getResult();

+            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..935a5cb
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.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.filter;

+

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

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

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

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

+

+/**

+ * ConsumerContextInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Extension("consumercontext")

+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).setUrl(invoker.getUrl());

+        }

+        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..55837d2
--- /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;

+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

+ */

+@Extension("context")

+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).setUrl(invoker.getUrl());

+        }

+        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..f61cda8
--- /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;

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

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

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

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

+

+/**

+ * DeprecatedInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Extension("deprecated")

+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..83fb1a1
--- /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;

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

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

+

+/**

+ * EchoInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Extension("echo")

+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..0a5de6f
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.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.rpc.filter;

+

+import java.lang.reflect.Method;

+

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

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

+import com.alibaba.dubbo.rpc.service.GenericService;

+

+/**

+ * ExceptionInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Extension("exception")

+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..30a56d0
--- /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.Extension;

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

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

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

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

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

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

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

+

+/**

+ * ThreadLimitInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Extension("executelimit")

+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..026ea0a
--- /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;
+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.Invoker;
+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;

+import com.alibaba.dubbo.rpc.service.GenericException;

+
+/**
+ * GenericInvokerFilter.
+ * 
+ * @author william.liangf
+ */
+@Extension("generic")
+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);
+                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.getResult()));
+            } 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..bd37336
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.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.filter;
+
+import java.lang.reflect.Constructor;

+import java.lang.reflect.Field;

+

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

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

+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
+ */
+@Extension("genericimpl")
+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.getResult();
+                try {
+                    return new RpcResult(PojoUtils.realize(value, invoker.getInterface().getMethod(methodName, parameterTypes).getReturnType()));
+                } 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..7bfabf9
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.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.filter;
+
+import java.util.Arrays;

+

+import com.alibaba.dubbo.common.Extension;
+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
+ */
+@Extension("timeout")
+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..6d2008c
--- /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;

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

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

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

+

+/**

+ * TokenInvokerFilter

+ * 

+ * @author william.liangf

+ */

+@Extension("token")

+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/listener/DeprecatedInvokerListener.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java
new file mode 100644
index 0000000..f3a689c
--- /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;

+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

+ */

+@Extension("deprecated")

+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..67a1052
--- /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..a144f2a
--- /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.setUrl(getUrl());
+        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..b1bd545
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.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.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;
+import com.alibaba.dubbo.rpc.RpcConstants;
+
+/**
+ * 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 = RpcConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT;
+        String value = ConfigUtils.getProperty(RpcConstants.SHUTDOWN_TIMEOUT_KEY);
+        if (value != null && value.length() > 0) {
+            try{
+                timeout = Integer.parseInt(value);
+            }catch (Exception e) {
+            }        
+        } else {
+            value = ConfigUtils.getProperty(RpcConstants.SHUTDOWN_TIMEOUT_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..de915dc
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.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;

+

+import java.util.ArrayList;

+import java.util.List;

+

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

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

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

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

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

+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, invoker.getUrl().getParameter(Constants.SERVICE_FILTER_KEY), RpcConstants.DEFAULT_SERVICE_FILTERS));

+    }

+

+    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), url.getParameter(Constants.REFERENCE_FILTER_KEY), RpcConstants.DEFAULT_REFERENCE_FILTERS);

+    }

+

+    public void destroy() {

+        protocol.destroy();

+    }

+

+    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String config, List<String> defaults) {

+        List<String> names = ConfigUtils.mergeValues(Filter.class, config, defaults);

+        Invoker<T> last = invoker;

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

+            List<Filter> filters = new ArrayList<Filter>(names.size());

+            for (String name : names) {

+                filters.add(ExtensionLoader.getExtensionLoader(Filter.class).getExtension(name));

+            }

+            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..369af61
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.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.protocol;

+

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.List;

+

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

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

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

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

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

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

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

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

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

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

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

+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), 

+                buildServiceListeners(invoker.getUrl().getParameter(Constants.EXPORTER_LISTENER_KEY), RpcConstants.DEFAULT_EXPORTER_LISTENERS));

+    }

+

+    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), 

+                buildReferenceListeners(url.getParameter(Constants.INVOKER_LISTENER_KEY), RpcConstants.DEFAULT_INVOKER_LISTENERS));

+    }

+

+    public void destroy() {

+        protocol.destroy();

+    }

+    

+    private static List<ExporterListener> buildServiceListeners(String config, List<String> defaults) {

+        List<String> names = ConfigUtils.mergeValues(Filter.class, config, defaults);

+        List<ExporterListener> listeners = new ArrayList<ExporterListener>();

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

+            for (String name : names) {

+                listeners.add(ExtensionLoader.getExtensionLoader(ExporterListener.class).getExtension(name));

+            }

+        }

+        return Collections.unmodifiableList(listeners);

+    }

+    

+    private static List<InvokerListener> buildReferenceListeners(String config, List<String> defaults) {

+        List<String> names = ConfigUtils.mergeValues(Filter.class, config, defaults);

+        List<InvokerListener> listeners = new ArrayList<InvokerListener>();

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

+            for (String name : names) {

+                listeners.add(ExtensionLoader.getExtensionLoader(InvokerListener.class).getExtension(name));

+            }

+        }

+        return Collections.unmodifiableList(listeners);

+    }

+

+}
\ 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..2101c79
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
@@ -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.

+ */

+package com.alibaba.dubbo.rpc.proxy.javassist;
+
+import com.alibaba.dubbo.common.Extension;

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

+import com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker;

+
+/**
+ * JavaassistRpcProxyFactory 
+
+ * @author william.liangf
+ */
+@Extension("javassist")
+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..dace6c2
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java
@@ -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.

+ */

+package com.alibaba.dubbo.rpc.proxy.jdk;
+
+import java.lang.reflect.Method;

+import java.lang.reflect.Proxy;

+

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

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

+import com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker;

+
+/**
+ * JavaassistRpcProxyFactory
+
+ * @author william.liangf
+ */
+@Extension("jdk")
+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/MockProxyFactoryWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyFactoryWrapper.java
new file mode 100644
index 0000000..653fe3d
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyFactoryWrapper.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.proxy.wrapper;

+

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

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

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

+

+/**

+ * MockProxyFactoryWrapper

+ * 

+ * @author william.liangf

+ */

+public class MockProxyFactoryWrapper implements ProxyFactory {

+    

+    private static final Logger LOGGER = LoggerFactory.getLogger(MockProxyFactoryWrapper.class);

+    

+    private final ProxyFactory proxyFactory;

+    

+    public MockProxyFactoryWrapper(ProxyFactory proxyFactory) {

+        this.proxyFactory = proxyFactory;

+    }

+    

+    @SuppressWarnings({ "unchecked"})

+    public <T> T getProxy(Invoker<T> invoker) throws RpcException {

+        final URL url = invoker.getUrl();

+        String mock = url.getParameterAndDecoded(Constants.MOCK_KEY);

+        boolean methodMock = url.hasMethodParameter(null, Constants.MOCK_KEY);

+        if ((methodMock || ConfigUtils.isNotEmpty(mock)) && GenericService.class != invoker.getInterface()) {

+            if (methodMock || mock.startsWith(Constants.RETURN_PREFIX)) {

+                invoker = new MockReturnInvoker<T>(invoker);

+            } else {

+                Class<?> serviceType = invoker.getInterface();

+                if (ConfigUtils.isDefault(mock)) {

+                    mock = serviceType.getName() + "Mock";

+                }

+                try {

+                    Class<?> mockClass = ReflectUtils.forName(mock);

+                    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 = new MockProxyInvoker<T>(invoker, proxyFactory.getInvoker(mockObject, invoker.getInterface(), invoker.getUrl()));

+                    } catch (InstantiationException e) {

+                        throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e);

+                    }

+                } catch (Throwable t) {

+                    LOGGER.error("Failed to create mock implemention class " + mock + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);

+                    // ignore

+                }

+            }

+        }

+        return proxyFactory.getProxy(invoker);

+    }

+    

+    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {

+        return proxyFactory.getInvoker(proxy, type, url);

+    }

+    

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyInvoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyInvoker.java
new file mode 100644
index 0000000..8a52a66
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyInvoker.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.proxy.wrapper;

+

+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.support.DelegateInvoker;

+

+/**

+ * MockProxyInvoker

+ * 

+ * @author william.liangf

+ */

+public class MockProxyInvoker<T> extends DelegateInvoker<T> {

+    

+    private final Invoker<T> mockInvoker;

+

+    public MockProxyInvoker(Invoker<T> invoker, Invoker<T> mockInvoker) {

+        super(invoker);

+        this.mockInvoker = mockInvoker;

+    }

+

+    public Result invoke(Invocation invocation) throws RpcException {

+        try {

+            return super.invoke(invocation);

+        } catch (RpcException e) {

+            return mockInvoker.invoke(invocation);

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockReturnInvoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockReturnInvoker.java
new file mode 100644
index 0000000..ae1e958
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockReturnInvoker.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.rpc.proxy.wrapper;

+

+import java.lang.reflect.Type;

+import java.util.List;

+import java.util.Map;

+import java.util.regex.Pattern;

+

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

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

+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.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.support.DelegateInvoker;

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

+

+/**

+ * MockReturnInvoker

+ * 

+ * @author william.liangf

+ */

+public class MockReturnInvoker<T> extends DelegateInvoker<T> {

+    

+    public MockReturnInvoker(Invoker<T> invoker) {

+        super(invoker);

+    }

+    

+    private static final Pattern NUMBER_PATTERN = Pattern.compile("^[0-9]");

+

+    public Result invoke(Invocation invocation) throws RpcException {

+        try {

+            return super.invoke(invocation);

+        } catch (RpcException e) {

+            String mock = getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY);

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

+                mock = URL.decode(mock);

+                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 e;

+        }

+    }

+    

+    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 (NUMBER_PATTERN.matcher(mock).matches()) {

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

+    }

+

+}
\ 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..47ad11d
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.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.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.RpcConstants;

+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(RpcConstants.STUB_EVENT_KEY, RpcConstants.DEFAULT_STUB_EVENT)){

+                            url = url.addParameter(RpcConstants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));

+                            url = url.addParameter(RpcConstants.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/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/RpcUtils.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java
new file mode 100644
index 0000000..f1fa653
--- /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.getUrl() != null

+                    && ! invocation.getMethodName().startsWith("$")) {

+                String service = invocation.getUrl().getServiceName();

+                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.getUrl() != null

+                    && ! invocation.getMethodName().startsWith("$")) {

+                String service = invocation.getUrl().getServiceName();

+                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/services/com.alibaba.dubbo.remoting.Codec b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec
new file mode 100644
index 0000000..5b10445
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec
@@ -0,0 +1 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler
new file mode 100644
index 0000000..6b17325
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler
@@ -0,0 +1,8 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ListTelnetHandler

+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler

+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler

+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler

+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler

+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler

+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.PortTelnetHandler

+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.LogTelnetHandler
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..a054250
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1,14 @@
+com.alibaba.dubbo.rpc.filter.EchoFilter

+com.alibaba.dubbo.rpc.filter.GenericFilter

+com.alibaba.dubbo.rpc.filter.GenericImplFilter

+com.alibaba.dubbo.rpc.filter.TokenFilter

+com.alibaba.dubbo.rpc.filter.AccessLogFilter

+com.alibaba.dubbo.rpc.filter.ActiveLimitFilter

+com.alibaba.dubbo.rpc.filter.ClassLoaderFilter

+com.alibaba.dubbo.rpc.filter.ContextFilter

+com.alibaba.dubbo.rpc.filter.ConsumerContextFilter

+com.alibaba.dubbo.rpc.filter.ExceptionFilter

+com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter

+com.alibaba.dubbo.rpc.filter.DeprecatedFilter

+com.alibaba.dubbo.rpc.filter.CompatibleFilter

+com.alibaba.dubbo.rpc.filter.TimeoutFilter
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.InvokerListener b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.InvokerListener
new file mode 100644
index 0000000..014211c
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.InvokerListener
@@ -0,0 +1 @@
+com.alibaba.dubbo.rpc.listener.DeprecatedInvokerListener
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..2793819
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
+com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.ProxyFactory b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.ProxyFactory
new file mode 100644
index 0000000..041e1c9
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.ProxyFactory
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.rpc.proxy.wrapper.MockProxyFactoryWrapper

+com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper

+com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory

+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..46e5940
--- /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.ExtensionLoader;

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

+

+/**

+ * 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..06571d7
--- /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.MockInvoker;

+

+/**

+ * AccessLogFilterTest.java

+ * 

+ * @author tony.chenl

+ */

+public class AccessLogFilterTest {

+

+    Filter accessLogFilter = new AccessLogFilter();

+

+    // 测试filter不会抛出异常

+    @Test

+    public void testInvokeException() {

+        Invoker<AccessLogFilterTest> invoker = new MockInvoker<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 MockInvoker<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 MockInvoker<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..30b2ddf
--- /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.MockInvoker;

+

+/**

+ * 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 MockInvoker<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 MockInvoker<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 MockInvoker<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..c4ec681
--- /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.setResult("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.setResult("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.setResult("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.getResult());

+    }

+

+    @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.setResult("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.getResult());

+    }

+    

+    @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.setResult(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.getResult());

+    }

+

+    @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.setResult("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.getResult());

+    }

+}
\ 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..5d861fc
--- /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.MockInvoker;

+

+/**

+ * 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 MockInvoker<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..c10c801
--- /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.MockInvoker;

+

+/**

+ * 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.setResult("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 MockInvoker<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..81cba99
--- /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.MockInvoker;

+

+/**

+ * 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 MockInvoker<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..00351d1
--- /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.setResult("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.getResult());

+    }

+

+    @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.setResult("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.getResult());

+    }

+}
\ 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/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..a0c48df
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.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.support;

+

+import java.util.HashMap;

+import java.util.Map;

+

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

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

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

+

+/**

+ * 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 URL getUrl() {

+        return null;

+    }

+

+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvoker.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
new file mode 100644
index 0000000..21c3171
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvoker.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 MockInvoker<T> implements Invoker<T> {

+

+    URL      url;

+    Class<T> type;

+    boolean  hasException = false;

+

+    public MockInvoker(URL url){

+        this.url = url;

+        type = (Class<T>) DemoService.class;

+    }

+

+    public MockInvoker(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.setResult("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/pom.xml b/dubbo/pom.xml
new file mode 100644
index 0000000..16f758e
--- /dev/null
+++ b/dubbo/pom.xml
@@ -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.

+-->

+<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.0.13</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-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-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-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.ServicesResourceTransformer" />

+							</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..2e17155
--- /dev/null
+++ b/pom.xml
@@ -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.

+-->

+<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.0.13</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>
+		<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>
+		<!-- 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>-Xms512m -Xmx512m</argline>
+		<skip_maven_deploy>false</skip_maven_deploy>
+		<updateReleaseInfo>true</updateReleaseInfo>
+	</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-cluster</module>
+		<module>dubbo-registry</module>
+		<module>dubbo-registry-default</module>
+		<module>dubbo-registry-multicast</module>
+		<module>dubbo-registry-zookeeper</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>
+	</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>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>
+			<!-- 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>
\ No newline at end of file