2.2.2 tag
git-svn-id: http://code.alibabatech.com/svn/dubbo/tags/2.2.2@1955 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/NOTICE b/NOTICE
new file mode 100644
index 0000000..9000982
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,66 @@
+Resources:
+
+ Wiki Home: http://code.alibabatech.com/wiki/display/dubbo
+ Training Document: http://code.alibabatech.com/docs/dubbo
+ Issue Tracking: http://code.alibabatech.com/jira/browse/DUBBO
+ Code Review: http://code.alibabatech.com/fisheye/graph/dubbo
+ Source Repository: http://code.alibabatech.com/svn/dubbo
+ Binary Repository: http://code.alibabatech.com/mvn/releases/com/alibaba/dubbo
+ Blog: http://code.alibabatech.com/blog
+ Micro Blog: http://weibo.com/dubbo
+ Mailling List: http://groups.google.com/group/dubbo
+
+Dependencies:
+
+ Required:
+ jdk: 1.5+
+ Default Optional:
+ spring: 2.5.6.SEC03
+ javassist: 3.15.0-GA
+ netty: 3.2.5.Final
+ commons-logging: 1.1.1
+ log4j: 1.2.16
+ Support Optional:
+ mina: 1.1.7
+ grizzly: 2.1.4
+ httpclient: 4.1.2
+ hessian_lite: 3.2.1-fixed
+ xstream: 1.4.1
+ fastjson: 1.1.8
+ bsf: 3.1
+ zookeeper: 3.3.3
+ jedis: 2.0.0
+ jfreechart: 1.0.13
+ hessian: 4.0.7
+ servlet: 2.5
+ jetty: 6.1.26
+ validation-api: 1.0.0.GA
+ hibernate-validator: 4.2.0.Final
+ jcache: 0.4
+
+Alternatives:
+
+ TaoBao HSF (Java): http://baike.corp.taobao.com/index.php/HSF
+ Snda Venus (Java): http://wiki.hexnova.com/display/Venus
+ Twitter Finagle (Scala): http://twitter.github.com/finagle
+ LinkedIn Norbert (Scala): http://sna-projects.com/norbert
+ Zeroc ICE (C++): http://www.zeroc.com/ice.html
+
+Developers:
+
+ LiangFei (William)
+ LiuChao (Charles)
+ LiDing (Jerry)
+ ChengLei (Tony)
+ LiuHaoMin (Ludvik)
+ LuGang (Kimi)
+
+Users:
+
+ alibaba.com
+ 1688.com
+ aliexpress.com
+ alibado.com
+ aliloan.com
+ lp.taobao.com
+ laiwang.com
diff --git a/README b/README
new file mode 100644
index 0000000..7a25bb3
--- /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.2.2-assembly.tar.gz
+ cd dubbo-demo-provider-2.2.2/bin
+ ./start.sh
+
+5. Install the demo consumer:
+
+ cd ~/dubbo/dubbo-demo-consumer/target
+ tar zxvf dubbo-demo-consumer-2.2.2-assembly.tar.gz
+ cd dubbo-demo-consumer-2.2.2/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.2.2-assembly.tar.gz
+ cd dubbo-simple-monitor-2.2.2/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.2.2-assembly.tar.gz
+ cd dubbo-simple-registry-2.2.2/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..346f74a
--- /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.2.2</version>
+ </parent>
+ <artifactId>dubbo-cluster</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bsf</groupId>
+ <artifactId>bsf-api</artifactId>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
new file mode 100644
index 0000000..039c43b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.support.FailoverCluster;
+
+/**
+ * Cluster. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Computer_cluster">Cluster</a>
+ * <a href="http://en.wikipedia.org/wiki/Fault-tolerant_system">Fault-Tolerant</a>
+ *
+ * @author william.liangf
+ */
+@SPI(FailoverCluster.NAME)
+public interface Cluster {
+
+ /**
+ * Merge the directory invokers to a virtual invoker.
+ *
+ * @param <T>
+ * @param directory
+ * @return cluster invoker
+ * @throws RpcException
+ */
+ @Adaptive
+ <T> Invoker<T> join(Directory<T> directory) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Configurator.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Configurator.java
new file mode 100644
index 0000000..74357f0
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Configurator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Configurator. (SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public interface Configurator extends Comparable<Configurator> {
+
+ /**
+ * get the configurator url.
+ *
+ * @return configurator url.
+ */
+ URL getUrl();
+
+ /**
+ * Configure the provider url.
+ * O
+ * @param url - old rovider url.
+ * @return new provider url.
+ */
+ URL configure(URL url);
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/ConfiguratorFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/ConfiguratorFactory.java
new file mode 100644
index 0000000..ae91854
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/ConfiguratorFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * ConfiguratorFactory. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface ConfiguratorFactory {
+
+ /**
+ * get the configurator instance.
+ *
+ * @param url - configurator url.
+ * @return configurator instance.
+ */
+ @Adaptive("protocol")
+ Configurator getConfigurator(URL url);
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java
new file mode 100644
index 0000000..1729b5a
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Node;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * Directory. (SPI, Prototype, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Directory_service">Directory Service</a>
+ *
+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
+ * @author william.liangf
+ */
+public interface Directory<T> extends Node {
+
+ /**
+ * get service type.
+ *
+ * @return service type.
+ */
+ Class<T> getInterface();
+
+ /**
+ * list invokers.
+ *
+ * @return invokers
+ */
+ List<Invoker<T>> list(Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java
new file mode 100644
index 0000000..d8290f5
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.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.cluster;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;
+
+/**
+ * LoadBalance. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
+ *
+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
+ * @author qian.lei
+ * @author william.liangf
+ */
+@SPI(RandomLoadBalance.NAME)
+public interface LoadBalance {
+
+ /**
+ * select one invoker in list.
+ *
+ * @param invokers invokers.
+ * @param url refer url
+ * @param invocation invocation.
+ * @return selected invoker.
+ */
+ @Adaptive("loadbalance")
+ <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Merger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Merger.java
new file mode 100644
index 0000000..2009f9e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Merger.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@SPI
+public interface Merger<T> {
+
+ T merge(T... items);
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
new file mode 100644
index 0000000..b5b5dc1
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.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;
+
+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;
+
+/**
+ * Router. (SPI, Prototype, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
+ *
+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
+ * @see com.alibaba.dubbo.rpc.cluster.Directory#list(Invocation)
+ * @author chao.liuc
+ */
+public interface Router extends Comparable<Router> {
+
+ /**
+ * get the router url.
+ *
+ * @return url
+ */
+ URL getUrl();
+
+ /**
+ * route.
+ *
+ * @param invokers
+ * @param url refer url
+ * @param invocation
+ * @return routed invokers
+ * @throws RpcException
+ */
+ <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java
new file mode 100644
index 0000000..b9b46b5
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * RouterFactory. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
+ *
+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
+ * @see com.alibaba.dubbo.rpc.cluster.Directory#list(Invocation)
+ * @author chao.liuc
+ */
+@SPI
+public interface RouterFactory {
+
+ /**
+ * Create router.
+ *
+ * @param url
+ * @return router
+ */
+ @Adaptive("protocol")
+ Router getRouter(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/AbstractConfigurator.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/AbstractConfigurator.java
new file mode 100644
index 0000000..40790b7
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/AbstractConfigurator.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.configurator;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.Configurator;
+
+/**
+ * AbstractOverrideConfigurator
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractConfigurator implements Configurator {
+
+ private final URL configuratorUrl;
+
+ public AbstractConfigurator(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("configurator url == null");
+ }
+ this.configuratorUrl = url;
+ }
+
+ public URL getUrl() {
+ return configuratorUrl;
+ }
+
+ public URL configure(URL url) {
+ if (configuratorUrl == null || configuratorUrl.getHost() == null
+ || url == null || url.getHost() == null) {
+ return url;
+ }
+ if (Constants.ANYHOST_VALUE.equals(configuratorUrl.getHost())
+ || url.getHost().equals(configuratorUrl.getHost())) {
+ String configApplication = configuratorUrl.getParameter(Constants.APPLICATION_KEY, configuratorUrl.getUsername());
+ String currentApplication = url.getParameter(Constants.APPLICATION_KEY, url.getUsername());
+ if (configApplication == null || Constants.ANY_VALUE.equals(configApplication)
+ || configApplication.equals(currentApplication)) {
+ if (configuratorUrl.getPort() == 0 || url.getPort() == configuratorUrl.getPort()) {
+ Set<String> condtionKeys = new HashSet<String>();
+ for (Map.Entry<String, String> entry : configuratorUrl.getParameters().entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key.startsWith("~") || Constants.APPLICATION_KEY.equals(key)
+ || Constants.SIDE_KEY.equals(key)) {
+ condtionKeys.add(key);
+ if (value != null && ! Constants.ANY_VALUE.equals(value)
+ && ! value.equals(url.getParameter(key.startsWith("~") ? key.substring(1) : key))) {
+ return url;
+ }
+ }
+ }
+ return doConfigure(url, configuratorUrl.removeParameters(condtionKeys));
+ }
+ }
+ }
+ return url;
+ }
+
+ public int compareTo(Configurator o) {
+ if (o == null) {
+ return -1;
+ }
+ return getUrl().getHost().compareTo(o.getUrl().getHost());
+ }
+
+ protected abstract URL doConfigure(URL currentUrl, URL configUrl);
+
+ public static void main(String[] args) {
+ System.out.println(URL.encode("timeout=100"));
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/absent/AbsentConfigurator.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/absent/AbsentConfigurator.java
new file mode 100644
index 0000000..76ac081
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/absent/AbsentConfigurator.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.configurator.absent;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.configurator.AbstractConfigurator;
+
+/**
+ * AbsentConfigurator
+ *
+ * @author william.liangf
+ */
+public class AbsentConfigurator extends AbstractConfigurator {
+
+ public AbsentConfigurator(URL url) {
+ super(url);
+ }
+
+ public URL doConfigure(URL currentUrl, URL configUrl) {
+ return currentUrl.addParametersIfAbsent(configUrl.getParameters());
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/absent/AbsentConfiguratorFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/absent/AbsentConfiguratorFactory.java
new file mode 100644
index 0000000..9889268
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/absent/AbsentConfiguratorFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.configurator.absent;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.Configurator;
+import com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory;
+
+/**
+ * AbsentConfiguratorFactory
+ *
+ * @author william.liangf
+ */
+public class AbsentConfiguratorFactory implements ConfiguratorFactory {
+
+ public Configurator getConfigurator(URL url) {
+ return new AbsentConfigurator(url);
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/override/OverrideConfigurator.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/override/OverrideConfigurator.java
new file mode 100644
index 0000000..b4f29af
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/override/OverrideConfigurator.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.configurator.override;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.configurator.AbstractConfigurator;
+
+/**
+ * AbsentConfigurator
+ *
+ * @author william.liangf
+ */
+public class OverrideConfigurator extends AbstractConfigurator {
+
+ public OverrideConfigurator(URL url) {
+ super(url);
+ }
+
+ public URL doConfigure(URL currentUrl, URL configUrl) {
+ return currentUrl.addParameters(configUrl.getParameters());
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/override/OverrideConfiguratorFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/override/OverrideConfiguratorFactory.java
new file mode 100644
index 0000000..6be2fea
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/configurator/override/OverrideConfiguratorFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.configurator.override;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.Configurator;
+import com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory;
+
+/**
+ * AbsentConfiguratorFactory
+ *
+ * @author william.liangf
+ */
+public class OverrideConfiguratorFactory implements ConfiguratorFactory {
+
+ public Configurator getConfigurator(URL url) {
+ return new OverrideConfigurator(url);
+ }
+
+}
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..72d81f7
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/AbstractDirectory.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.rpc.cluster.directory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.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.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+import com.alibaba.dubbo.rpc.cluster.router.MockInvokersSelector;
+
+/**
+ * 增加router的Directory
+ *
+ * @author chao.liuc
+ */
+public abstract class AbstractDirectory<T> implements Directory<T> {
+
+ // 日志输出
+ private static final Logger logger = LoggerFactory.getLogger(AbstractDirectory.class);
+
+ private final URL url ;
+
+ private volatile boolean destroyed = false;
+
+ private volatile List<Router> routers;
+
+ public AbstractDirectory(URL url) {
+ this(url, null);
+ }
+
+ public AbstractDirectory(URL url, List<Router> routers) {
+ if (url == null)
+ throw new IllegalArgumentException("url == null");
+ this.url = url;
+ setRouters(routers);
+ }
+
+ public List<Invoker<T>> list(Invocation invocation) throws RpcException {
+ if (destroyed){
+ throw new RpcException("Directory already destroyed .url: "+ getUrl());
+ }
+ List<Invoker<T>> invokers = doList(invocation);
+ List<Router> localRouters = this.routers; // local reference
+ if (localRouters != null && localRouters.size() > 0) {
+ for (Router router: localRouters){
+ try {
+ if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) {
+ invokers = router.route(invokers, getUrl(), invocation);
+ }
+ } catch (Throwable t) {
+ logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ return invokers;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public List<Router> getRouters(){
+ return routers;
+ }
+
+ protected void setRouters(List<Router> routers){
+ // copy list
+ routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers);
+ // append url router
+ 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));
+ }
+ // append mock invoker selector
+ routers.add(new MockInvokersSelector());
+ this.routers = routers;
+ }
+
+ public boolean isDestroyed() {
+ return destroyed;
+ }
+
+ public void destroy(){
+ destroyed = true;
+ }
+
+ protected abstract List<Invoker<T>> doList(Invocation invocation) throws RpcException ;
+
+}
\ 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..56adee0
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.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.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 (isDestroyed()) {
+ return false;
+ }
+ for (Invoker<T> invoker : invokers) {
+ if (invoker.isAvailable()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void destroy() {
+ if(isDestroyed()) {
+ 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..f7adbb1
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.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.loadbalance;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+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, URL url, Invocation invocation) {
+ if (invokers == null || invokers.size() == 0)
+ return null;
+ if (invokers.size() == 1)
+ return invokers.get(0);
+ return doSelect(invokers, url, invocation);
+ }
+
+ protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);
+
+ protected int getWeight(Invoker<?> invoker, Invocation invocation) {
+ return invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java
new file mode 100644
index 0000000..30512f9
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * ConsistentHashLoadBalance
+ *
+ * @author william.liangf
+ */
+public class ConsistentHashLoadBalance extends AbstractLoadBalance {
+
+ private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
+ String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
+ int identityHashCode = System.identityHashCode(invokers);
+ ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
+ if (selector == null || selector.getIdentityHashCode() != identityHashCode) {
+ selectors.put(key, new ConsistentHashSelector<T>(invokers, invocation.getMethodName(), identityHashCode));
+ selector = (ConsistentHashSelector<T>) selectors.get(key);
+ }
+ return selector.select(invocation);
+ }
+
+ private static final class ConsistentHashSelector<T> {
+
+ private final TreeMap<Long, Invoker<T>> virtualInvokers;
+
+ private final int replicaNumber;
+
+ private final int identityHashCode;
+
+ private final int[] argumentIndex;
+
+ public ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
+ this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
+ this.identityHashCode = System.identityHashCode(invokers);
+ URL url = invokers.get(0).getUrl();
+ this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);
+ String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0"));
+ argumentIndex = new int[index.length];
+ for (int i = 0; i < index.length; i ++) {
+ argumentIndex[i] = Integer.parseInt(index[i]);
+ }
+ for (Invoker<T> invoker : invokers) {
+ for (int i = 0; i < replicaNumber / 4; i++) {
+ byte[] digest = md5(invoker.getUrl().toFullString() + i);
+ for (int h = 0; h < 4; h++) {
+ long m = hash(digest, h);
+ virtualInvokers.put(m, invoker);
+ }
+ }
+ }
+ }
+
+ public int getIdentityHashCode() {
+ return identityHashCode;
+ }
+
+ public Invoker<T> select(Invocation invocation) {
+ String key = toKey(invocation.getArguments());
+ byte[] digest = md5(key);
+ Invoker<T> invoker = sekectForKey(hash(digest, 0));
+ return invoker;
+ }
+
+ private String toKey(Object[] args) {
+ StringBuilder buf = new StringBuilder();
+ for (int i : argumentIndex) {
+ if (i >= 0 && i < args.length) {
+ buf.append(args[i]);
+ }
+ }
+ return buf.toString();
+ }
+
+ private Invoker<T> sekectForKey(long hash) {
+ Invoker<T> invoker;
+ Long key = hash;
+ if (!virtualInvokers.containsKey(key)) {
+ SortedMap<Long, Invoker<T>> tailMap = virtualInvokers.tailMap(key);
+ if (tailMap.isEmpty()) {
+ key = virtualInvokers.firstKey();
+ } else {
+ key = tailMap.firstKey();
+ }
+ }
+ invoker = virtualInvokers.get(key);
+ return invoker;
+ }
+
+ private long hash(byte[] digest, int number) {
+ return (((long) (digest[3 + number * 4] & 0xFF) << 24)
+ | ((long) (digest[2 + number * 4] & 0xFF) << 16)
+ | ((long) (digest[1 + number * 4] & 0xFF) << 8)
+ | (digest[0 + number * 4] & 0xFF))
+ & 0xFFFFFFFFL;
+ }
+
+ private byte[] md5(String value) {
+ MessageDigest md5;
+ try {
+ md5 = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ md5.reset();
+ byte[] bytes = null;
+ try {
+ bytes = value.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ md5.update(bytes);
+ return md5.digest();
+ }
+
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java
new file mode 100644
index 0000000..c9ebf79
--- /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.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcStatus;
+
+/**
+ * LeastActiveLoadBalance
+ *
+ * @author william.liangf
+ */
+public class LeastActiveLoadBalance extends AbstractLoadBalance {
+
+ public static final String NAME = "leastactive";
+
+ private final Random random = new Random();
+
+ protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, 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/RandomLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
new file mode 100644
index 0000000..de5002c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.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.cluster.loadbalance;
+
+import java.util.List;
+import java.util.Random;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * random load balance.
+ *
+ * @author qianlei
+ * @author william.liangf
+ */
+public class RandomLoadBalance extends AbstractLoadBalance {
+
+ public static final String NAME = "random";
+
+ private final Random random = new Random();
+
+ protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
+ int length = invokers.size(); // 总个数
+ int totalWeight = 0; // 总权重
+ boolean sameWeight = true; // 权重是否都一样
+ for (int i = 0; i < length; i++) {
+ int weight = getWeight(invokers.get(i), invocation);
+ totalWeight += weight; // 累计总权重
+ if (sameWeight && i > 0
+ && weight != getWeight(invokers.get(i - 1), invocation)) {
+ sameWeight = false; // 计算所有权重是否一样
+ }
+ }
+ if (totalWeight > 0 && ! sameWeight) {
+ // 如果权重不相同且权重大于0则按总权重数随机
+ int offset = random.nextInt(totalWeight);
+ // 并确定随机值落在哪个片断上
+ for (int i = 0; i < length; i++) {
+ offset -= getWeight(invokers.get(i), invocation);
+ if (offset < 0) {
+ return invokers.get(i);
+ }
+ }
+ }
+ // 如果权重相同或权重为0则均等随机
+ return invokers.get(random.nextInt(length));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java
new file mode 100644
index 0000000..ff65499
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.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.rpc.cluster.loadbalance;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.AtomicPositiveInteger;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * Round robin load balance.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class RoundRobinLoadBalance extends AbstractLoadBalance {
+
+ public static final String NAME = "roundrobin";
+
+ private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
+
+ private final ConcurrentMap<String, AtomicPositiveInteger> weightSequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
+
+ protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
+ String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
+ int length = invokers.size(); // 总个数
+ int maxWeight = 0; // 最大权重
+ int minWeight = Integer.MAX_VALUE; // 最小权重
+ for (int i = 0; i < length; i++) {
+ int weight = getWeight(invokers.get(i), invocation);
+ maxWeight = Math.max(maxWeight, weight); // 累计最大权重
+ minWeight = Math.min(minWeight, weight); // 累计最小权重
+ }
+ if (maxWeight > 0 && minWeight < maxWeight) { // 权重不一样
+ AtomicPositiveInteger weightSequence = weightSequences.get(key);
+ if (weightSequence == null) {
+ weightSequences.putIfAbsent(key, new AtomicPositiveInteger());
+ weightSequence = weightSequences.get(key);
+ }
+ int currentWeight = weightSequence.getAndIncrement() % maxWeight;
+ List<Invoker<T>> weightInvokers = new ArrayList<Invoker<T>>();
+ for (Invoker<T> invoker : invokers) { // 筛选权重大于当前权重基数的Invoker
+ if (getWeight(invoker, invocation) > currentWeight) {
+ weightInvokers.add(invoker);
+ }
+ }
+ int weightLength = weightInvokers.size();
+ if (weightLength == 1) {
+ return weightInvokers.get(0);
+ } else if (weightLength > 1) {
+ invokers = weightInvokers;
+ length = invokers.size();
+ }
+ }
+ AtomicPositiveInteger sequence = sequences.get(key);
+ if (sequence == null) {
+ sequences.putIfAbsent(key, new AtomicPositiveInteger());
+ sequence = sequences.get(key);
+ }
+ // 取模轮循
+ return invokers.get(sequence.getAndIncrement() % length);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ArrayMerger.java
new file mode 100644
index 0000000..37de9f1
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ArrayMerger.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.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+import java.lang.reflect.Array;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class ArrayMerger implements Merger<Object[]> {
+
+ public static final ArrayMerger INSTANCE = new ArrayMerger();
+
+ public Object[] merge(Object[]... others) {
+ if (others.length == 0) {
+ return null;
+ }
+ int totalLen = 0;
+ for (int i = 0; i < others.length; i++) {
+ Object item = others[i];
+ if (item != null && item.getClass().isArray()) {
+ totalLen += Array.getLength(item);
+ } else {
+ throw new IllegalArgumentException(
+ new StringBuilder(32).append(i + 1)
+ .append("th argument is not an array").toString());
+ }
+ }
+
+ if (totalLen == 0) {
+ return null;
+ }
+
+ Class<?> type = others[0].getClass().getComponentType();
+
+ Object result = Array.newInstance(type, totalLen);
+ int index = 0;
+ for (Object array : others) {
+ for (int i = 0; i < Array.getLength(array); i++) {
+ Array.set(result, index++, Array.get(array, i));
+ }
+ }
+ return (Object[])result;
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/BooleanArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/BooleanArrayMerger.java
new file mode 100644
index 0000000..0c73a61
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/BooleanArrayMerger.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class BooleanArrayMerger implements Merger<boolean[]> {
+
+ public boolean[] merge(boolean[]... items) {
+ int totalLen = 0;
+ for(boolean[] array : items) {
+ totalLen += array.length;
+ }
+ boolean[] result = new boolean[totalLen];
+ int index = 0;
+ for(boolean[] array : items) {
+ for(boolean item : array) {
+ result[index++] = item;
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ByteArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ByteArrayMerger.java
new file mode 100644
index 0000000..4cd4777
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ByteArrayMerger.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class ByteArrayMerger implements Merger<byte[]>{
+
+ public byte[] merge(byte[]... items) {
+ int total = 0;
+ for (byte[] array : items) {
+ total += array.length;
+ }
+ byte[] result = new byte[total];
+ int index = 0;
+ for (byte[] array : items) {
+ for (byte item : array) {
+ result[index++] = item;
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/CharArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/CharArrayMerger.java
new file mode 100644
index 0000000..0410bd2
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/CharArrayMerger.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class CharArrayMerger implements Merger<char[]> {
+
+ public char[] merge(char[]... items) {
+ int total = 0;
+ for (char[] array : items) {
+ total += array.length;
+ }
+ char[] result = new char[total];
+ int index = 0;
+ for (char[] array : items) {
+ for (char item : array) {
+ result[index++] = item;
+ }
+ }
+ return result;
+ }
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/DoubleArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/DoubleArrayMerger.java
new file mode 100644
index 0000000..1c69917
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/DoubleArrayMerger.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class DoubleArrayMerger implements Merger<double[]> {
+
+ public double[] merge(double[]... items) {
+ int total = 0;
+ for (double[] array : items) {
+ total += array.length;
+ }
+ double[] result = new double[total];
+ int index = 0;
+ for (double[] array : items) {
+ for (double item : array) {
+ result[index++] = item;
+ }
+ }
+ return result;
+ }
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/FloatArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/FloatArrayMerger.java
new file mode 100644
index 0000000..18eeabb
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/FloatArrayMerger.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class FloatArrayMerger implements Merger<float[]> {
+
+ public float[] merge(float[]... items) {
+ int total = 0;
+ for (float[] array : items) {
+ total += array.length;
+ }
+ float[] result = new float[total];
+ int index = 0;
+ for (float[] array : items) {
+ for (float item : array) {
+ result[index++] = item;
+ }
+ }
+ return result;
+ }
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/IntArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/IntArrayMerger.java
new file mode 100644
index 0000000..ca016b6
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/IntArrayMerger.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class IntArrayMerger implements Merger<int[]>{
+
+ public int[] merge(int[]... items) {
+ int totalLen = 0;
+ for( int[] item : items ) {
+ totalLen += item.length;
+ }
+ int[] result = new int[totalLen];
+ int index = 0;
+ for(int[] item : items) {
+ for(int i : item) {
+ result[index++] = i;
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ListMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ListMerger.java
new file mode 100644
index 0000000..537a3fc
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ListMerger.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class ListMerger implements Merger<List<?>> {
+
+ public List<Object> merge(List<?>... items) {
+ List<Object> result = new ArrayList<Object>();
+ for (List<?> item : items) {
+ if (item != null) {
+ result.addAll(item);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/LongArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/LongArrayMerger.java
new file mode 100644
index 0000000..249246b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/LongArrayMerger.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class LongArrayMerger implements Merger<long[]> {
+
+ public long[] merge(long[]... items) {
+ int total = 0;
+ for (long[] array : items) {
+ total += array.length;
+ }
+ long[] result = new long[total];
+ int index = 0;
+ for (long[] array : items) {
+ for (long item : array) {
+ result[index++] = item;
+ }
+ }
+ return result;
+ }
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/MapMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/MapMerger.java
new file mode 100644
index 0000000..ddd042c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/MapMerger.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.cluster.merger;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MapMerger implements Merger<Map<?, ?>> {
+
+ public Map<?, ?> merge(Map<?, ?>... items) {
+ if (items.length == 0) {
+ return null;
+ }
+ Map<Object, Object> result = new HashMap<Object, Object>();
+ for (Map<?, ?> item : items) {
+ if (item != null) {
+ result.putAll(item);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/MergerFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/MergerFactory.java
new file mode 100644
index 0000000..c9e8f0a
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/MergerFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MergerFactory {
+
+ private static final ConcurrentMap<Class<?>, Merger<?>> mergerCache =
+ new ConcurrentHashMap<Class<?>, Merger<?>>();
+
+ public static <T> Merger<T> getMerger(Class<T> returnType) {
+ Merger result;
+ if (returnType.isArray()) {
+ Class type = returnType.getComponentType();
+ result = mergerCache.get(type);
+ if (result == null) {
+ loadMergers();
+ result = mergerCache.get(type);
+ }
+ if(result == null && ! type.isPrimitive()) {
+ result = ArrayMerger.INSTANCE;
+ }
+ } else {
+ result = mergerCache.get(returnType);
+ if (result == null) {
+ loadMergers();
+ result = mergerCache.get(returnType);
+ }
+ }
+ return result;
+ }
+
+ static void loadMergers() {
+ Set<String> names = ExtensionLoader.getExtensionLoader(Merger.class)
+ .getSupportedExtensions();
+ for (String name : names) {
+ Merger m = ExtensionLoader.getExtensionLoader(Merger.class).getExtension(name);
+ mergerCache.putIfAbsent(ReflectUtils.getGenericClass(m.getClass()), m);
+ }
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/SetMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/SetMerger.java
new file mode 100644
index 0000000..9d8eb6d
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/SetMerger.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class SetMerger implements Merger<Set<?>> {
+
+ public Set<Object> merge(Set<?>... items) {
+
+ Set<Object> result = new HashSet<Object>();
+
+ for (Set<?> item : items) {
+ if (item != null) {
+ result.addAll(item);
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ShortArrayMerger.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ShortArrayMerger.java
new file mode 100644
index 0000000..75a9a3e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/merger/ShortArrayMerger.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.cluster.merger;
+
+import com.alibaba.dubbo.rpc.cluster.Merger;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class ShortArrayMerger implements Merger<short[]> {
+
+ public short[] merge(short[]... items) {
+ int total = 0;
+ for (short[] array : items) {
+ total += array.length;
+ }
+ short[] result = new short[total];
+ int index = 0;
+ for (short[] array : items) {
+ for (short item : array) {
+ result[index++] = item;
+ }
+ }
+ return result;
+ }
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.java
new file mode 100644
index 0000000..8ec3a2b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.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.cluster.router;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Router;
+
+/**
+ * mock invoker选择器
+ * @author chao.liuc
+ *
+ */
+public class MockInvokersSelector implements Router {
+
+ public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,
+ URL url, final Invocation invocation) throws RpcException {
+ if (invocation.getAttachments() == null) {
+ return getNormalInvokers(invokers);
+ } else {
+ String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);
+ if (value == null)
+ return getNormalInvokers(invokers);
+ else if (Boolean.TRUE.toString().equalsIgnoreCase(value)){
+ return getMockedInvokers(invokers);
+ }
+ }
+ return invokers;
+ }
+
+ private <T> List<Invoker<T>> getMockedInvokers(final List<Invoker<T>> invokers) {
+ if (! hasMockProviders(invokers)){
+ return null;
+ }
+ List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(1);
+ for (Invoker<T> invoker : invokers){
+ if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)){
+ sInvokers.add(invoker);
+ }
+ }
+ return sInvokers;
+ }
+
+ private <T> List<Invoker<T>> getNormalInvokers(final List<Invoker<T>> invokers){
+ if (! hasMockProviders(invokers)){
+ return invokers;
+ } else {
+ List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(invokers.size());
+ for (Invoker<T> invoker : invokers){
+ if (! invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)){
+ sInvokers.add(invoker);
+ }
+ }
+ return sInvokers;
+ }
+ }
+
+ private <T> boolean hasMockProviders(final List<Invoker<T>> invokers){
+ boolean hasMockProvider = false;
+ for (Invoker<T> invoker : invokers){
+ if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)){
+ hasMockProvider = true;
+ break;
+ }
+ }
+ return hasMockProvider;
+ }
+
+ public URL getUrl() {
+ return null;
+ }
+
+ public int compareTo(Router o) {
+ return 1;
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouter.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouter.java
new file mode 100644
index 0000000..d9bca59
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouter.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router.condition;
+
+import java.text.ParseException;
+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.regex.Matcher;
+import java.util.regex.Pattern;
+
+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.common.utils.UrlUtils;
+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;
+
+/**
+ * ConditionRouter
+ *
+ * @author william.liangf
+ */
+public class ConditionRouter implements Router, Comparable<Router> {
+
+ private static final Logger logger = LoggerFactory.getLogger(ConditionRouter.class);
+
+ private final URL url;
+
+ private final int priority;
+
+ private final boolean force;
+
+ private final Map<String, MatchPair> whenCondition;
+
+ private final Map<String, MatchPair> thenCondition;
+
+ public ConditionRouter(URL url) {
+ this.url = url;
+ this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
+ this.force = url.getParameter(Constants.FORCE_KEY, false);
+ try {
+ String rule = url.getParameterAndDecoded(Constants.RULE_KEY);
+ if (rule == null || rule.trim().length() == 0) {
+ throw new IllegalArgumentException("Illegal route rule!");
+ }
+ rule = rule.replace("consumer.", "").replace("provider.", "");
+ int i = rule.indexOf("=>");
+ String whenRule = i < 0 ? null : rule.substring(0, i).trim();
+ String thenRule = i < 0 ? rule.trim() : rule.substring(i + 2).trim();
+ Map<String, MatchPair> when = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap<String, MatchPair>() : parseRule(whenRule);
+ Map<String, MatchPair> then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? null : parseRule(thenRule);
+ // NOTE: When条件是允许为空的,外部业务来保证类似的约束条件
+ this.whenCondition = when;
+ this.thenCondition = then;
+ } catch (ParseException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
+ throws RpcException {
+ if (invokers == null || invokers.size() == 0) {
+ return invokers;
+ }
+ try {
+ if (! matchWhen(url)) {
+ return invokers;
+ }
+ List<Invoker<T>> result = new ArrayList<Invoker<T>>();
+ if (thenCondition == null) {
+ logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
+ return result;
+ }
+ for (Invoker<T> invoker : invokers) {
+ if (matchThen(invoker.getUrl(), url)) {
+ result.add(invoker);
+ }
+ }
+ if (result.size() > 0) {
+ return result;
+ } else if (force) {
+ logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(Constants.RULE_KEY));
+ return result;
+ }
+ } catch (Throwable t) {
+ logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
+ }
+ return invokers;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public int compareTo(Router o) {
+ if (o == null || o.getClass() != ConditionRouter.class) {
+ return 1;
+ }
+ ConditionRouter c = (ConditionRouter) o;
+ return this.priority == c.priority ? url.toFullString().compareTo(c.url.toFullString()) : (this.priority > c.priority ? 1 : -1);
+ }
+
+ public boolean matchWhen(URL url) {
+ return matchCondition(whenCondition, url, null);
+ }
+
+ public boolean matchThen(URL url, URL param) {
+ return thenCondition != null && matchCondition(thenCondition, url, param);
+ }
+
+ private boolean matchCondition(Map<String, MatchPair> condition, URL url, URL param) {
+ Map<String, String> sample = url.toMap();
+ for (Map.Entry<String, String> entry : sample.entrySet()) {
+ String key = entry.getKey();
+ MatchPair pair = condition.get(key);
+ if (pair != null && ! pair.isMatch(entry.getValue(), param)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)");
+
+ private static Map<String, MatchPair> parseRule(String rule)
+ throws ParseException {
+ Map<String, MatchPair> condition = new HashMap<String, MatchPair>();
+ if(StringUtils.isBlank(rule)) {
+ return condition;
+ }
+ // 匹配或不匹配Key-Value对
+ MatchPair pair = null;
+ // 多个Value值
+ Set<String> values = null;
+ final Matcher matcher = ROUTE_PATTERN.matcher(rule);
+ while (matcher.find()) { // 逐个匹配
+ String separator = matcher.group(1);
+ String content = matcher.group(2);
+ // 表达式开始
+ if (separator == null || separator.length() == 0) {
+ pair = new MatchPair();
+ condition.put(content, pair);
+ }
+ // KV开始
+ else if ("&".equals(separator)) {
+ if (condition.get(content) == null) {
+ pair = new MatchPair();
+ condition.put(content, pair);
+ } else {
+ condition.put(content, pair);
+ }
+ }
+ // KV的Value部分开始
+ else if ("=".equals(separator)) {
+ if (pair == null)
+ throw new ParseException("Illegal route rule \""
+ + rule + "\", The error char '" + separator
+ + "' at index " + matcher.start() + " before \""
+ + content + "\".", matcher.start());
+
+ values = pair.matches;
+ values.add(content);
+ }
+ // KV的Value部分开始
+ else if ("!=".equals(separator)) {
+ if (pair == null)
+ throw new ParseException("Illegal route rule \""
+ + rule + "\", The error char '" + separator
+ + "' at index " + matcher.start() + " before \""
+ + content + "\".", matcher.start());
+
+ values = pair.mismatches;
+ values.add(content);
+ }
+ // KV的Value部分的多个条目
+ else if (",".equals(separator)) { // 如果为逗号表示
+ if (values == null || values.size() == 0)
+ throw new ParseException("Illegal route rule \""
+ + rule + "\", The error char '" + separator
+ + "' at index " + matcher.start() + " before \""
+ + content + "\".", matcher.start());
+ values.add(content);
+ } else {
+ throw new ParseException("Illegal route rule \"" + rule
+ + "\", The error char '" + separator + "' at index "
+ + matcher.start() + " before \"" + content + "\".", matcher.start());
+ }
+ }
+ return condition;
+ }
+
+ private static final class MatchPair {
+ final Set<String> matches = new HashSet<String>();
+ final Set<String> mismatches = new HashSet<String>();
+ public boolean isMatch(String value, URL param) {
+ for (String match : matches) {
+ if (! UrlUtils.isMatchGlobPattern(match, value, param)) {
+ return false;
+ }
+ }
+ for (String mismatch : mismatches) {
+ if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java
new file mode 100644
index 0000000..a12ec3e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router.condition;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+/**
+ * ConditionRouterFactory
+ *
+ * @author william.liangf
+ */
+public class ConditionRouterFactory implements RouterFactory {
+
+ public static final String NAME = "condition";
+
+ public Router getRouter(URL url) {
+ return new ConditionRouter(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterFactory.java
new file mode 100644
index 0000000..f22ebc9
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterFactory.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.rpc.cluster.router.file;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.IOUtils;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+import com.alibaba.dubbo.rpc.cluster.router.script.ScriptRouterFactory;
+
+public class FileRouterFactory implements RouterFactory {
+
+ public static final String NAME = "file";
+
+ private RouterFactory routerFactory;
+
+ public void setRouterFactory(RouterFactory routerFactory) {
+ this.routerFactory = routerFactory;
+ }
+
+ public Router getRouter(URL url) {
+ try {
+ // File URL 转换成 其它Route URL,然后Load
+ // file:///d:/path/to/route.js?router=script ==> script:///d:/path/to/route.js?type=js&rule=<file-content>
+ String protocol = url.getParameter(Constants.ROUTER_KEY, ScriptRouterFactory.NAME); // 将原类型转为协议
+ String type = null; // 使用文件后缀做为类型
+ String path = url.getPath();
+ if (path != null) {
+ int i = path.lastIndexOf('.');
+ if (i > 0) {
+ type = path.substring(i + 1);
+ }
+ }
+ String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath())));
+ URL script = url.setProtocol(protocol).addParameter(Constants.TYPE_KEY, type).addParameterAndEncoded(Constants.RULE_KEY, rule);
+
+ return routerFactory.getRouter(script);
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouter.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouter.java
new file mode 100644
index 0000000..35ce000
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router.script;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Router;
+
+/**
+ * ScriptRouter
+ *
+ * @author william.liangf
+ */
+public class ScriptRouter implements Router {
+
+ private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class);
+
+ private static final Map<String, ScriptEngine> engines = new ConcurrentHashMap<String, ScriptEngine>();
+
+ private final ScriptEngine engine;
+
+ private final int priority;
+
+ private final String rule;
+
+ private final URL url;
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public ScriptRouter(URL url) {
+ this.url = url;
+ String type = url.getParameter(Constants.TYPE_KEY);
+ this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
+ String rule = url.getParameterAndDecoded(Constants.RULE_KEY);
+ if (type == null || type.length() == 0){
+ type = Constants.DEFAULT_SCRIPT_TYPE_KEY;
+ }
+ if (rule == null || rule.length() == 0){
+ throw new IllegalStateException(new IllegalStateException("route rule can not be empty. rule:" + rule));
+ }
+ ScriptEngine engine = engines.get(type);
+ if (engine == null){
+ engine = new ScriptEngineManager().getEngineByName(type);
+ if (engine == null) {
+ throw new IllegalStateException(new IllegalStateException("Unsupported route rule type: " + type + ", rule: " + rule));
+ }
+ engines.put(type, engine);
+ }
+ this.engine = engine;
+ this.rule = rule;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, 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 + ", method:" + invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e);
+ return invokers;
+ }
+ }
+
+ public int compareTo(Router o) {
+ if (o == null || o.getClass() != ScriptRouter.class) {
+ return 1;
+ }
+ ScriptRouter c = (ScriptRouter) o;
+ return this.priority == c.priority ? rule.compareTo(c.rule) : (this.priority > c.priority ? 1 : -1);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouterFactory.java
new file mode 100644
index 0000000..9f7c92c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouterFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router.script;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+/**
+ * ScriptRouterFactory
+ *
+ * Script Router Factory用到的URL形如:
+ * <ol>
+ * <li> script://registyAddress?type=js&rule=xxxx
+ * <li> script:///path/to/routerfile.js?type=js&rule=xxxx
+ * <li> script://D:\path\to\routerfile.js?type=js&rule=xxxx
+ * <li> script://C:/path/to/routerfile.js?type=js&rule=xxxx
+ * </ol>
+ * URL的Host一段包含的是Script Router内容的来源,Registry、File etc
+ *
+ * @author william.liangf
+ */
+public class ScriptRouterFactory implements RouterFactory {
+
+ public static final String NAME = "script";
+
+ public Router getRouter(URL url) {
+ return new ScriptRouter(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
new file mode 100644
index 0000000..bb895fa
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.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.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.support.RpcUtils;
+
+/**
+ * AbstractClusterInvoker
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public abstract class AbstractClusterInvoker<T> implements Invoker<T> {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(AbstractClusterInvoker.class);
+ protected final Directory<T> directory;
+
+ protected final boolean availablecheck;
+
+ private volatile boolean destroyed = false;
+
+ private volatile Invoker<T> stickyInvoker = null;
+
+ public AbstractClusterInvoker(Directory<T> directory) {
+ this(directory, directory.getUrl());
+ }
+
+ public AbstractClusterInvoker(Directory<T> directory, URL url) {
+ if (directory == null)
+ throw new IllegalArgumentException("service directory == null");
+
+ this.directory = directory ;
+ //sticky 需要检测 avaliablecheck
+ this.availablecheck = url.getParameter(Constants.CLUSTER_AVAILABLE_CHECK_KEY, Constants.DEFAULT_CLUSTER_AVAILABLE_CHECK) ;
+ }
+
+ public Class<T> getInterface() {
+ return directory.getInterface();
+ }
+
+ public URL getUrl() {
+ return directory.getUrl();
+ }
+
+ public boolean isAvailable() {
+ Invoker<T> invoker = stickyInvoker;
+ if (invoker != null) {
+ return invoker.isAvailable();
+ }
+ return directory.isAvailable();
+ }
+
+ public void destroy() {
+ directory.destroy();
+ destroyed = true;
+ }
+
+ /**
+ * 使用loadbalance选择invoker.</br>
+ * a)先lb选择,如果在selected列表中 或者 不可用且做检验时,进入下一步(重选),否则直接返回</br>
+ * b)重选验证规则:selected > available .保证重选出的结果尽量不在select中,并且是可用的
+ *
+ * @param availablecheck 如果设置true,在选择的时候先选invoker.available == true
+ * @param selected 已选过的invoker.注意:输入保证不重复
+ *
+ */
+ protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
+ if (invokers == null || invokers.size() == 0)
+ return null;
+ String methodName = invocation == null ? "" : invocation.getMethodName();
+
+ boolean sticky = invokers.get(0).getUrl().getMethodParameter(methodName,Constants.CLUSTER_STICKY_KEY, Constants.DEFAULT_CLUSTER_STICKY) ;
+ {
+ //ignore overloaded method
+ if ( stickyInvoker != null && !invokers.contains(stickyInvoker) ){
+ stickyInvoker = null;
+ }
+ //ignore cucurrent problem
+ if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))){
+ if (availablecheck && stickyInvoker.isAvailable()){
+ return stickyInvoker;
+ }
+ }
+ }
+ Invoker<T> invoker = doselect(loadbalance, invocation, invokers, selected);
+
+ if (sticky){
+ stickyInvoker = invoker;
+ }
+ return invoker;
+ }
+
+ private Invoker<T> doselect(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
+ if (invokers == null || invokers.size() == 0)
+ return null;
+ if (invokers.size() == 1)
+ return invokers.get(0);
+ // 如果只有两个invoker,退化成轮循
+ if (invokers.size() == 2 && selected != null && selected.size() > 0) {
+ return selected.get(0) == invokers.get(0) ? invokers.get(1) : invokers.get(0);
+ }
+ Invoker<T> invoker = loadbalance.select(invokers, getUrl(), 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, getUrl(), 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, getUrl(), 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, getUrl(), invocation);
+ }
+ }
+ return null;
+ }
+
+ public Result invoke(final Invocation invocation) throws RpcException {
+
+ checkWheatherDestoried();
+
+ LoadBalance loadbalance;
+
+ List<Invoker<T>> invokers = 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);
+ }
+ RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
+ return doInvoke(invocation, invokers, loadbalance);
+ }
+
+ protected void checkWheatherDestoried() {
+
+ if(destroyed){
+ throw new RpcException("Rpc cluster invoker for " + getInterface() + " on consumer " + NetUtils.getLocalHost()
+ + " use dubbo version " + Version.getVersion()
+ + " is now destroyed! Can not invoke any more.");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getInterface() + " -> " + getUrl().toString();
+ }
+
+ protected void checkInvokers(List<Invoker<T>> invokers, Invocation invocation) {
+ if (invokers == null || invokers.size() == 0) {
+ throw new RpcException("Failed to invoke the method "
+ + invocation.getMethodName() + " in the service " + getInterface().getName()
+ + ". No provider available for the service " + directory.getUrl().getServiceKey()
+ + " from registry " + directory.getUrl().getAddress()
+ + " on the consumer " + NetUtils.getLocalHost()
+ + " using the dubbo version " + Version.getVersion()
+ + ". Please check if the providers have been started and registered.");
+ }
+ }
+
+ protected abstract Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
+ LoadBalance loadbalance) throws RpcException;
+
+ protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
+ List<Invoker<T>> invokers = directory.list(invocation);
+ return invokers;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java
new file mode 100644
index 0000000..7b0de21
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AvailableCluster
+ *
+ * @author william.liangf
+ */
+public class AvailableCluster implements Cluster {
+
+ public static final String NAME = "available";
+
+ public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+
+ return new AbstractClusterInvoker<T>(directory) {
+ public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ for (Invoker<T> invoker : invokers) {
+ if (invoker.isAvailable()) {
+ return invoker.invoke(invocation);
+ }
+ }
+ throw new RpcException("No provider available in " + invokers);
+ }
+ };
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
new file mode 100644
index 0000000..98cb106
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AvailableCluster
+ *
+ * @author william.liangf
+ */
+public class AvailableClusterInvoker<T> extends AbstractClusterInvoker<T> {
+
+ public AvailableClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ for (Invoker<T> invoker : invokers) {
+ if (invoker.isAvailable()) {
+ return invoker.invoke(invocation);
+ }
+ }
+ throw new RpcException("No provider available in " + invokers);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastCluster.java
new file mode 100644
index 0000000..907ec78
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastCluster.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * BroadcastCluster
+ *
+ * @author william.liangf
+ */
+public class BroadcastCluster implements Cluster {
+
+ public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+ return new BroadcastClusterInvoker<T>(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
new file mode 100644
index 0000000..e5d7412
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * BroadcastClusterInvoker
+ *
+ * @author william.liangf
+ */
+public class BroadcastClusterInvoker<T> extends AbstractClusterInvoker<T> {
+
+ private static final Logger logger = LoggerFactory.getLogger(BroadcastClusterInvoker.class);
+
+ public BroadcastClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ checkInvokers(invokers, invocation);
+ RpcContext.getContext().setInvokers((List)invokers);
+ RpcException exception = null;
+ Result result = null;
+ for (Invoker<T> invoker: invokers) {
+ try {
+ result = invoker.invoke(invocation);
+ } catch (RpcException e) {
+ exception = e;
+ logger.warn(e.getMessage(), e);
+ } catch (Throwable e) {
+ exception = new RpcException(e.getMessage(), e);
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java
new file mode 100644
index 0000000..feadc3e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * ClusterUtils
+ *
+ * @author william.liangf
+ */
+public class ClusterUtils {
+
+ public static URL mergeUrl(URL remoteUrl, Map<String, String> localMap) {
+ Map<String, String> map = new HashMap<String, String>();
+ Map<String, String> remoteMap = remoteUrl.getParameters();
+
+
+ if (remoteMap != null && remoteMap.size() > 0) {
+ map.putAll(remoteMap);
+
+ //线程池配置不使用提供者的
+ map.remove(Constants.THREAD_NAME_KEY);
+ map.remove(Constants.THREADS_KEY);
+ map.remove(Constants.QUEUES_KEY);
+ map.remove(Constants.THREAD_ALIVE_KEY);
+ }
+
+ if (localMap != null && localMap.size() > 0) {
+ map.putAll(localMap);
+ }
+ if (remoteMap != null && remoteMap.size() > 0) {
+ // 版本号使用提供者的
+ String dubbo = remoteMap.get(Constants.DUBBO_VERSION_KEY);
+ if (dubbo != null && dubbo.length() > 0) {
+ map.put(Constants.DUBBO_VERSION_KEY, dubbo);
+ }
+ String version = remoteMap.get(Constants.VERSION_KEY);
+ if (version != null && version.length() > 0) {
+ map.put(Constants.VERSION_KEY, version);
+ }
+ String group = remoteMap.get(Constants.GROUP_KEY);
+ if (group != null && group.length() > 0) {
+ map.put(Constants.GROUP_KEY, group);
+ }
+ String methods = remoteMap.get(Constants.METHODS_KEY);
+ if (methods != null && methods.length() > 0) {
+ map.put(Constants.METHODS_KEY, methods);
+ }
+ // 合并filter和listener
+ String remoteFilter = remoteMap.get(Constants.REFERENCE_FILTER_KEY);
+ String localFilter = localMap.get(Constants.REFERENCE_FILTER_KEY);
+ if (remoteFilter != null && remoteFilter.length() > 0
+ && localFilter != null && localFilter.length() > 0) {
+ localMap.put(Constants.REFERENCE_FILTER_KEY, remoteFilter + "," + localFilter);
+ }
+ String remoteListener = remoteMap.get(Constants.INVOKER_LISTENER_KEY);
+ String localListener = localMap.get(Constants.INVOKER_LISTENER_KEY);
+ if (remoteListener != null && remoteListener.length() > 0
+ && localListener != null && localListener.length() > 0) {
+ localMap.put(Constants.INVOKER_LISTENER_KEY, remoteListener + "," + localListener);
+ }
+ }
+ return remoteUrl.addParameters(map);
+ }
+
+ private ClusterUtils() {}
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.java
new file mode 100644
index 0000000..ac28746
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作。
+ *
+ * <a href="http://en.wikipedia.org/wiki/Failback">Failback</a>
+ *
+ * @author william.liangf
+ */
+public class FailbackCluster implements Cluster {
+
+ public final static String NAME = "failback";
+
+ public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+ return new FailbackClusterInvoker<T>(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
new file mode 100644
index 0000000..cbf51a5
--- /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 method " + invocation.getMethodName() + ", 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 method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: "
+ + e.getMessage() + ", ", e);
+ addFailed(invocation, this);
+ return new RpcResult(); // ignore
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java
new file mode 100644
index 0000000..4f39e25
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。
+ *
+ * <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a>
+ *
+ * @author william.liangf
+ */
+public class FailfastCluster implements Cluster {
+
+ public final static String NAME = "failfast";
+
+ public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+ return new FailfastClusterInvoker<T>(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
new file mode 100644
index 0000000..d5a2f59
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。
+ *
+ * <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a>
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class FailfastClusterInvoker<T> extends AbstractClusterInvoker<T>{
+
+ public FailfastClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ checkInvokers(invokers, invocation);
+ Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+ try {
+ return invoker.invoke(invocation);
+ } catch (Throwable e) {
+ if (e instanceof RpcException && ((RpcException)e).isBiz()) { // biz exception.
+ throw (RpcException) e;
+ }
+ throw new RpcException(e instanceof RpcException ? ((RpcException)e).getCode() : 0, "Failfast invoke providers " + invoker.getUrl() + " " + loadbalance.getClass().getSimpleName() + " select from all providers " + invokers + " for service " + getInterface().getName() + " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e.getCause() != null ? e.getCause() : e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.java
new file mode 100644
index 0000000..773b292
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。
+ *
+ * <a href="http://en.wikipedia.org/wiki/Failover">Failover</a>
+ *
+ * @author william.liangf
+ */
+public class FailoverCluster implements Cluster {
+
+ public final static String NAME = "failover";
+
+ public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+ return new FailoverClusterInvoker<T>(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
new file mode 100644
index 0000000..71c7f28
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.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.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
+ * @author chao.liuc
+ */
+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, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ List<Invoker<T>> copyinvokers = invokers;
+ checkInvokers(copyinvokers, 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>>(copyinvokers.size()); // invoked invokers.
+ Set<String> providers = new HashSet<String>(len);
+ for (int i = 0; i < len; i++) {
+ //重试时,进行重新选择,避免重试时invoker列表已发生变化.
+ //注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变
+ if (i > 0) {
+ checkWheatherDestoried();
+ copyinvokers = list(invocation);
+ //重新检查一下
+ checkInvokers(copyinvokers, invocation);
+ }
+ Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, 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() + "/" + copyinvokers.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() + "/" + copyinvokers.size()
+ + ") from the registry " + directory.getUrl().getAddress()
+ + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ + Version.getVersion() + ". Last error is: "
+ + (le != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.java
new file mode 100644
index 0000000..972960f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。
+ *
+ * <a href="http://en.wikipedia.org/wiki/Fail-safe">Fail-safe</a>
+ *
+ * @author william.liangf
+ */
+public class FailsafeCluster implements Cluster {
+
+ public final static String NAME = "failsafe";
+
+ public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+ return new FailsafeClusterInvoker<T>(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
new file mode 100644
index 0000000..5ddebfa
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。
+ *
+ * <a href="http://en.wikipedia.org/wiki/Fail-safe">Fail-safe</a>
+ *
+ * @author william.liangf
+ */
+public class FailsafeClusterInvoker<T> extends AbstractClusterInvoker<T>{
+ private static final Logger logger = LoggerFactory.getLogger(FailsafeClusterInvoker.class);
+
+ public FailsafeClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ try {
+ checkInvokers(invokers, invocation);
+ Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+ return invoker.invoke(invocation);
+ } catch (Throwable e) {
+ logger.error("Failsafe ignore exception: " + e.getMessage(), e);
+ return new RpcResult(); // ignore
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.java
new file mode 100644
index 0000000..f147efd
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。
+ *
+ * <a href="http://en.wikipedia.org/wiki/Fork_(topology)">Fork</a>
+ *
+ * @author william.liangf
+ */
+public class ForkingCluster implements Cluster {
+
+ public final static String NAME = "forking";
+
+ public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+ return new ForkingClusterInvoker<T>(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
new file mode 100644
index 0000000..1ee93ec
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。
+ *
+ * <a href="http://en.wikipedia.org/wiki/Fork_(topology)">Fork</a>
+ *
+ * @author william.liangf
+ */
+public class ForkingClusterInvoker<T> extends AbstractClusterInvoker<T>{
+
+ private final ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory("forking-cluster-timer", true));
+
+ public ForkingClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ checkInvokers(invokers, invocation);
+ final List<Invoker<T>> selected;
+ final int forks = getUrl().getParameter(Constants.FORKS_KEY, Constants.DEFAULT_FORKS);
+ final int timeout = getUrl().getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ if (forks <= 0 || forks >= invokers.size()) {
+ selected = invokers;
+ } else {
+ selected = new ArrayList<Invoker<T>>();
+ for (int i = 0; i < forks; i++) {
+ //在invoker列表(排除selected)后,如果没有选够,则存在重复循环问题.见select实现.
+ Invoker<T> invoker = select(loadbalance, invocation, invokers, selected);
+ if(!selected.contains(invoker)){//防止重复添加invoker
+ selected.add(invoker);
+ }
+ }
+ }
+ RpcContext.getContext().setInvokers((List)selected);
+ final AtomicInteger count = new AtomicInteger();
+ final BlockingQueue<Object> ref = new LinkedBlockingQueue<Object>();
+ for (final Invoker<T> invoker : selected) {
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ Result result = invoker.invoke(invocation);
+ ref.offer(result);
+ } catch(Throwable e) {
+ int value = count.incrementAndGet();
+ if (value >= selected.size()) {
+ ref.offer(e);
+ }
+ }
+ }
+ });
+ }
+ try {
+ Object ret = ref.poll(timeout, TimeUnit.MILLISECONDS);
+ if (ret instanceof Throwable) {
+ Throwable e = (Throwable) ret;
+ throw new RpcException(e instanceof RpcException ? ((RpcException)e).getCode() : 0, "Failed to forking invoke provider " + selected + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e.getCause() != null ? e.getCause() : e);
+ }
+ return (Result) ret;
+ } catch (InterruptedException e) {
+ throw new RpcException("Failed to forking invoke provider " + selected + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableCluster.java
new file mode 100644
index 0000000..29ebbc4
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableCluster.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MergeableCluster implements Cluster {
+
+ public static final String NAME = "mergeable";
+
+ public <T> Invoker<T> join( Directory<T> directory ) throws RpcException {
+ return new MergeableClusterInvoker<T>( directory );
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvoker.java
new file mode 100644
index 0000000..e00e209
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvoker.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.Merger;
+import com.alibaba.dubbo.rpc.cluster.merger.MergerFactory;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@SuppressWarnings( "unchecked" )
+public class MergeableClusterInvoker<T> implements Invoker<T> {
+
+ private static final Logger log = LoggerFactory.getLogger(MergeableClusterInvoker.class);
+
+ private ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory("mergeable-cluster-executor", true));
+
+ private final Directory<T> directory;
+
+ public MergeableClusterInvoker(Directory<T> directory) {
+ this.directory = directory;
+ }
+
+ public Result invoke(final Invocation invocation) throws RpcException {
+ List<Invoker<T>> invokers = directory.list(invocation);
+
+ String merger = getUrl().getMethodParameter( invocation.getMethodName(), Constants.MERGER_KEY );
+ if ( ConfigUtils.isEmpty(merger) ) { // 如果方法不需要Merge,退化为只调一个Group
+ for(final Invoker<T> invoker : invokers ) {
+ if (invoker.isAvailable()) {
+ return invoker.invoke(invocation);
+ }
+ }
+ return invokers.iterator().next().invoke(invocation);
+ }
+
+ Class<?> returnType;
+ try {
+ returnType = getInterface().getMethod(
+ invocation.getMethodName(), invocation.getParameterTypes() ).getReturnType();
+ } catch ( NoSuchMethodException e ) {
+ returnType = null;
+ }
+
+ Map<String, Future<Result>> results = new HashMap<String, Future<Result>>();
+ for( final Invoker<T> invoker : invokers ) {
+ Future<Result> future = executor.submit( new Callable<Result>() {
+ public Result call() throws Exception {
+ return invoker.invoke(new RpcInvocation(invocation, invoker));
+ }
+ } );
+ results.put( invoker.getUrl().getServiceKey(), future );
+ }
+
+ Object result = null;
+
+ List<Result> resultList = new ArrayList<Result>( results.size() );
+
+ int timeout = getUrl().getMethodParameter( invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT );
+ for ( Map.Entry<String, Future<Result>> entry : results.entrySet() ) {
+ Future<Result> future = entry.getValue();
+ try {
+ Result r = future.get( );
+ if (r.hasException()) {
+ log.error(new StringBuilder(32).append("Invoke ")
+ .append(getGroupDescFromServiceKey(entry.getKey()))
+ .append(" failed: ")
+ .append(r.getException().getMessage()).toString(),
+ r.getException());
+ } else {
+ resultList.add(r);
+ }
+ } catch ( Exception e ) {
+ throw new RpcException( new StringBuilder( 32 )
+ .append( "Failed to invoke service " )
+ .append( entry.getKey() )
+ .append( ": " )
+ .append( e.getMessage() ).toString(),
+ e );
+ }
+ }
+
+ if (resultList.size() == 0) {
+ return new RpcResult((Object)null);
+ } else if (resultList.size() == 1) {
+ return resultList.iterator().next();
+ }
+
+ if (returnType == void.class) {
+ return new RpcResult((Object)null);
+ }
+
+ if ( merger.startsWith(".") ) {
+ merger = merger.substring(1);
+ Method method;
+ try {
+ method = returnType.getMethod( merger, returnType );
+ } catch ( NoSuchMethodException e ) {
+ throw new RpcException( new StringBuilder( 32 )
+ .append( "Can not merge result because missing method [ " )
+ .append( merger )
+ .append( " ] in class [ " )
+ .append( returnType.getClass().getName() )
+ .append( " ]" )
+ .toString() );
+ }
+ if ( method != null ) {
+ if ( !Modifier.isPublic( method.getModifiers() ) ) {
+ method.setAccessible( true );
+ }
+ result = resultList.remove( 0 ).getValue();
+ try {
+ if ( method.getReturnType() != void.class
+ && method.getReturnType().isAssignableFrom( result.getClass() ) ) {
+ for ( Result r : resultList ) {
+ result = method.invoke( result, r.getValue() );
+ }
+ } else {
+ for ( Result r : resultList ) {
+ method.invoke( result, r.getValue() );
+ }
+ }
+ } catch ( Exception e ) {
+ throw new RpcException(
+ new StringBuilder( 32 )
+ .append( "Can not merge result: " )
+ .append( e.getMessage() ).toString(),
+ e );
+ }
+ } else {
+ throw new RpcException(
+ new StringBuilder( 32 )
+ .append( "Can not merge result because missing method [ " )
+ .append( merger )
+ .append( " ] in class [ " )
+ .append( returnType.getClass().getName() )
+ .append( " ]" )
+ .toString() );
+ }
+ } else {
+ Merger resultMerger;
+ if (ConfigUtils.isDefault(merger)) {
+ resultMerger = MergerFactory.getMerger(returnType);
+ } else {
+ resultMerger = ExtensionLoader.getExtensionLoader(Merger.class).getExtension(merger);
+ }
+ if (resultMerger != null) {
+ List<Object> rets = new ArrayList<Object>(resultList.size());
+ for(Result r : resultList) {
+ rets.add(r.getValue());
+ }
+ result = resultMerger.merge(
+ rets.toArray((Object[])Array.newInstance(returnType, 0)));
+ } else {
+ throw new RpcException( "There is no merger to merge result." );
+ }
+ }
+ return new RpcResult( result );
+ }
+
+ public Class<T> getInterface() {
+ return directory.getInterface();
+ }
+
+ public URL getUrl() {
+ return directory.getUrl();
+ }
+
+ public boolean isAvailable() {
+ return directory.isAvailable();
+ }
+
+ public void destroy() {
+ directory.destroy();
+ }
+
+ private String getGroupDescFromServiceKey(String key) {
+ int index = key.indexOf("/");
+ if (index > 0) {
+ return new StringBuilder(32).append("group [ ")
+ .append(key.substring(0, index)).append(" ]").toString();
+ }
+ return key;
+ }
+}
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java
new file mode 100644
index 0000000..886f5ba
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.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.rpc.cluster.support.wrapper;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.support.MockInvoker;
+
+/**
+ * @author chao.liuc
+ */
+public class MockClusterInvoker<T> implements Invoker<T>{
+
+ private static final Logger logger = LoggerFactory.getLogger(MockClusterInvoker.class);
+
+ private final Directory<T> directory ;
+
+ private final Invoker<T> invoker;
+
+ public MockClusterInvoker(Directory<T> directory, Invoker<T> invoker) {
+ this.directory = directory;
+ this.invoker = invoker;
+ }
+
+ public URL getUrl() {
+ return directory.getUrl();
+ }
+
+ public boolean isAvailable() {
+ return directory.isAvailable();
+ }
+
+ public void destroy() {
+ this.invoker.destroy();
+ }
+
+ public Class<T> getInterface() {
+ return directory.getInterface();
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ Result result = null;
+
+ String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
+ if (value.length() == 0 || value.equalsIgnoreCase("false")){
+ //no mock
+ result = this.invoker.invoke(invocation);
+ } else if (value.startsWith("force")) {
+ if (logger.isWarnEnabled()) {
+ logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
+ }
+ //force:direct mock
+ result = doMockInvoke(invocation, null);
+ } else {
+ //fail-mock
+ try {
+ result = this.invoker.invoke(invocation);
+ }catch (RpcException e) {
+ if (e.isBiz()) {
+ throw e;
+ } else {
+ if (logger.isWarnEnabled()) {
+ logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
+ }
+ result = doMockInvoke(invocation, e);
+ }
+ }
+ }
+ return result;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private Result doMockInvoke(Invocation invocation,RpcException e){
+ Result result = null;
+ Invoker<T> minvoker ;
+
+ List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
+ if (mockInvokers == null || mockInvokers.size() == 0){
+ minvoker = (Invoker<T>) new MockInvoker(directory.getUrl());
+ } else {
+ minvoker = mockInvokers.get(0);
+ }
+ try {
+ result = minvoker.invoke(invocation);
+ } catch (RpcException me) {
+ if (me.isBiz()) {
+ result = new RpcResult(me.getCause());
+ } else {
+ throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());
+ }
+//
+ } catch (Throwable me) {
+ throw new RpcException(getMockExceptionMessage(e, me), me.getCause());
+ }
+ return result;
+ }
+
+ private String getMockExceptionMessage(Throwable t, Throwable mt){
+ String msg = "mock error : " + mt.getMessage();
+ if (t != null){
+ msg = msg + ", invoke error is :" + StringUtils.toString(t);
+ }
+ return msg;
+ }
+
+ /**
+ * 返回MockInvoker
+ * 契约:
+ * directory根据invocation中是否有Constants.INVOCATION_NEED_MOCK,来判断获取的是一个normal invoker 还是一个 mock invoker
+ * 如果directorylist 返回多个mock invoker,只使用第一个invoker.
+ * @param invocation
+ * @return
+ */
+ private List<Invoker<T>> selectMockInvoker(Invocation invocation){
+ //TODO generic invoker?
+ if (invocation instanceof RpcInvocation){
+ //存在隐含契约(虽然在接口声明中增加描述,但扩展性会存在问题.同时放在attachement中的做法需要改进
+ ((RpcInvocation)invocation).setAttachment(Constants.INVOCATION_NEED_MOCK, Boolean.TRUE.toString());
+ //directory根据invocation中attachment是否有Constants.INVOCATION_NEED_MOCK,来判断获取的是normal invokers or mock invokers
+ List<Invoker<T>> invokers = directory.list(invocation);
+ return invokers;
+ } else {
+ return null ;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "invoker :" + this.invoker + ",directory: " + this.directory;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
new file mode 100644
index 0000000..065dcef
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support.wrapper;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * mock impl
+ *
+ * @author chao.liuc
+ *
+ */
+public class MockClusterWrapper implements Cluster {
+
+ private Cluster cluster;
+
+ public MockClusterWrapper(Cluster cluster) {
+ this.cluster = cluster;
+ }
+
+ public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+ return new MockClusterInvoker<T>(directory,
+ this.cluster.join(directory));
+ }
+
+}
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster
new file mode 100644
index 0000000..54e85ed
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster
@@ -0,0 +1,9 @@
+mock=com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper
+failover=com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
+failfast=com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
+failsafe=com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
+failback=com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
+forking=com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
+available=com.alibaba.dubbo.rpc.cluster.support.AvailableCluster
+mergeable=com.alibaba.dubbo.rpc.cluster.support.MergeableCluster
+broadcast=com.alibaba.dubbo.rpc.cluster.support.BroadcastCluster
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory
new file mode 100644
index 0000000..19236c0
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory
@@ -0,0 +1,2 @@
+override=com.alibaba.dubbo.rpc.cluster.configurator.override.OverrideConfiguratorFactory
+absent=com.alibaba.dubbo.rpc.cluster.configurator.absent.AbsentConfiguratorFactory
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance
new file mode 100644
index 0000000..de8a7f3
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance
@@ -0,0 +1,4 @@
+random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
+roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
+leastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
+consistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger
new file mode 100644
index 0000000..06d9264
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger
@@ -0,0 +1,11 @@
+map=com.alibaba.dubbo.rpc.cluster.merger.ListMerger
+set=com.alibaba.dubbo.rpc.cluster.merger.SetMerger
+list=com.alibaba.dubbo.rpc.cluster.merger.MapMerger
+byte=com.alibaba.dubbo.rpc.cluster.merger.ByteArrayMerger
+char=com.alibaba.dubbo.rpc.cluster.merger.CharArrayMerger
+short=com.alibaba.dubbo.rpc.cluster.merger.ShortArrayMerger
+int=com.alibaba.dubbo.rpc.cluster.merger.IntArrayMerger
+long=com.alibaba.dubbo.rpc.cluster.merger.LongArrayMerger
+float=com.alibaba.dubbo.rpc.cluster.merger.FloatArrayMerger
+double=com.alibaba.dubbo.rpc.cluster.merger.DoubleArrayMerger
+boolean=com.alibaba.dubbo.rpc.cluster.merger.BooleanArrayMerger
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory
new file mode 100644
index 0000000..239c6f0
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory
@@ -0,0 +1,3 @@
+file=com.alibaba.dubbo.rpc.cluster.router.file.FileRouterFactory
+script=com.alibaba.dubbo.rpc.cluster.router.script.ScriptRouterFactory
+condition=com.alibaba.dubbo.rpc.cluster.router.condition.ConditionRouterFactory
\ 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..5d769c9
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.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.rpc.cluster;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+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);
+ RpcInvocation 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 = new RpcInvocation();
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(StickyTest.class).anyTimes();
+ EasyMock.replay(dic);
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+
+ clusterinvoker = new StickyClusterInvoker<StickyTest>(dic);
+ }
+ URL url = URL.valueOf("test://test:11/test?"
+ +"&loadbalance=roundrobin"
+// +"&"+Constants.CLUSTER_AVAILABLE_CHECK_KEY+"=true"
+ +"&"+Constants.CLUSTER_STICKY_KEY+"=true"
+ );
+
+ int runs = 1;
+ @Test
+ public void testStickyNoCheck() {
+ int count = testSticky(null,false);
+ System.out.println(count);
+ Assert.assertTrue(count>0 && count <=runs);
+ }
+
+ @Test
+ public void testStickyForceCheck() {
+ int count = testSticky(null,true);
+ Assert.assertTrue(count == 0 || count == runs);
+ }
+ @Test
+ public void testMethodStickyNoCheck() {
+ int count = testSticky("method1",false);
+ System.out.println(count);
+ Assert.assertTrue(count>0 && count <=runs);
+ }
+
+ @Test
+ public void testMethodStickyForceCheck() {
+ int count = testSticky("method1",true);
+ Assert.assertTrue(count == 0 || count == runs);
+ }
+
+ @Test
+ public void testMethodsSticky() {
+ for(int i = 0 ;i<100 ; i++){//多次调用看两个方法是否都选在同一个invoker
+ int count1 = testSticky("method1",true);
+ int count2 = testSticky("method2",true);
+ Assert.assertTrue(count1 == count2);
+ }
+ }
+
+ public int testSticky(String methodName, boolean check) {
+ if (methodName == null){
+ url = url.addParameter(Constants.CLUSTER_STICKY_KEY, String.valueOf(check));
+ }else {
+ url = url.addParameter(methodName+"."+Constants.CLUSTER_STICKY_KEY, String.valueOf(check));
+ }
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(StickyTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(StickyTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+
+ invocation.setMethodName(methodName);
+
+ 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/configurator/absent/AbsentConfiguratorTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/configurator/absent/AbsentConfiguratorTest.java
new file mode 100644
index 0000000..07207e6
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/configurator/absent/AbsentConfiguratorTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.configurator.absent;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * OverrideConfiguratorTest
+ *
+ * @author william.liangf
+ */
+public class AbsentConfiguratorTest {
+
+ @Test
+ public void testOverride_Application(){
+ AbsentConfigurator configurator = new AbsentConfigurator(URL.valueOf("override://foo@0.0.0.0/com.foo.BarService?timeout=200"));
+
+ URL url = configurator.configure(URL.valueOf("dubbo://10.20.153.10:20880/com.foo.BarService?application=foo"));
+ Assert.assertEquals("200", url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.10:20880/com.foo.BarService?application=foo&timeout=1000"));
+ Assert.assertEquals("1000", url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.11:20880/com.foo.BarService?application=bar"));
+ Assert.assertNull(url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.11:20880/com.foo.BarService?application=bar&timeout=1000"));
+ Assert.assertEquals("1000", url.getParameter("timeout"));
+ }
+
+ @Test
+ public void testOverride_Host(){
+ AbsentConfigurator configurator = new AbsentConfigurator(URL.valueOf("override://10.20.153.10/com.foo.BarService?timeout=200"));
+
+ URL url = configurator.configure(URL.valueOf("dubbo://10.20.153.10:20880/com.foo.BarService?application=foo"));
+ Assert.assertEquals("200", url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.10:20880/com.foo.BarService?application=foo&timeout=1000"));
+ Assert.assertEquals("1000", url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.11:20880/com.foo.BarService?application=bar"));
+ Assert.assertNull(url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.11:20880/com.foo.BarService?application=bar&timeout=1000"));
+ Assert.assertEquals("1000", url.getParameter("timeout"));
+ }
+
+}
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/configurator/override/OverrideConfiguratorTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/configurator/override/OverrideConfiguratorTest.java
new file mode 100644
index 0000000..9a5da97
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/configurator/override/OverrideConfiguratorTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.configurator.override;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * OverrideConfiguratorTest
+ *
+ * @author william.liangf
+ */
+public class OverrideConfiguratorTest {
+
+ @Test
+ public void testOverride_Application(){
+ OverrideConfigurator configurator = new OverrideConfigurator(URL.valueOf("override://foo@0.0.0.0/com.foo.BarService?timeout=200"));
+
+ URL url = configurator.configure(URL.valueOf("dubbo://10.20.153.10:20880/com.foo.BarService?application=foo"));
+ Assert.assertEquals("200", url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.10:20880/com.foo.BarService?application=foo&timeout=1000"));
+ Assert.assertEquals("200", url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.11:20880/com.foo.BarService?application=bar"));
+ Assert.assertNull(url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.11:20880/com.foo.BarService?application=bar&timeout=1000"));
+ Assert.assertEquals("1000", url.getParameter("timeout"));
+ }
+
+ @Test
+ public void testOverride_Host(){
+ OverrideConfigurator configurator = new OverrideConfigurator(URL.valueOf("override://10.20.153.10/com.foo.BarService?timeout=200"));
+
+ URL url = configurator.configure(URL.valueOf("dubbo://10.20.153.10:20880/com.foo.BarService?application=foo"));
+ Assert.assertEquals("200", url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.10:20880/com.foo.BarService?application=foo&timeout=1000"));
+ Assert.assertEquals("200", url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.11:20880/com.foo.BarService?application=bar"));
+ Assert.assertNull(url.getParameter("timeout"));
+
+ url = configurator.configure(URL.valueOf("dubbo://10.20.153.11:20880/com.foo.BarService?application=bar&timeout=1000"));
+ Assert.assertEquals("1000", url.getParameter("timeout"));
+ }
+
+}
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..cc09aba
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * RoundRobinLoadBalanceTest
+ * @author liuchao
+ *
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public class LoadBalanceTest {
+ Invocation invocation ;
+ List<Invoker<LoadBalanceTest>> invokers = new ArrayList<Invoker<LoadBalanceTest>>();
+ Invoker<LoadBalanceTest> invoker1 ;
+ Invoker<LoadBalanceTest> invoker2 ;
+ Invoker<LoadBalanceTest> invoker3 ;
+ Invoker<LoadBalanceTest> invoker4 ;
+ Invoker<LoadBalanceTest> invoker5 ;
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+
+
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+
+ invoker1 = EasyMock.createMock(Invoker.class);
+ invoker2 = EasyMock.createMock(Invoker.class);
+ invoker3 = EasyMock.createMock(Invoker.class);
+ invoker4 = EasyMock.createMock(Invoker.class);
+ invoker5 = EasyMock.createMock(Invoker.class);
+
+ URL url = URL.valueOf("test://127.0.0.1/DemoService");
+
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker3.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker4.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker4.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker4.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker5.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker5.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker5.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.replay(invocation,invoker1,invoker2,invoker3,invoker4,invoker5);
+
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ invokers.add(invoker4);
+ invokers.add(invoker5);
+ }
+
+ @Test
+ public void testRoundRobinLoadBalance_select() {
+ int runs = 10000;
+ Map<Invoker,AtomicLong> counter = getInvokeCounter(runs,RoundRobinLoadBalance.NAME);
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+ Assert.assertTrue("abs diff shoud < 1", Math.abs(count-runs/(0f+invokers.size())) <1f);
+ }
+ }
+ @Test
+ public void testRandomLoadBalance_select() {
+ int runs = 1000;
+ Map<Invoker,AtomicLong> counter = getInvokeCounter(runs,RandomLoadBalance.NAME);
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+// System.out.println(count);
+ Assert.assertTrue("abs diff shoud < avg", Math.abs(count-runs/(0f+invokers.size())) <runs/(0f+invokers.size()));
+ }
+ }
+ @Test
+ public void testLeastActiveLoadBalance_select() {
+ int runs = 10000;
+ Map<Invoker,AtomicLong> counter = getInvokeCounter(runs,LeastActiveLoadBalance.NAME);
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+// System.out.println(count);
+ Assert.assertTrue("abs diff shoud < avg", Math.abs(count-runs/(0f+invokers.size())) <runs/(0f+invokers.size()));
+ }
+ }
+
+
+ public Map<Invoker,AtomicLong> getInvokeCounter(int runs,String loadbalanceName) {
+ Map<Invoker,AtomicLong> counter = new ConcurrentHashMap<Invoker,AtomicLong>();
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName);
+ for(Invoker invoker :invokers){
+ counter.put(invoker, new AtomicLong(0));
+ }
+ for(int i=0;i<runs;i++){
+ Invoker sinvoker = lb.select(invokers, invokers.get(0).getUrl(), invocation);
+ counter.get(sinvoker).incrementAndGet();
+ }
+ return counter;
+ }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/merger/ResultMergerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/merger/ResultMergerTest.java
new file mode 100644
index 0000000..2312fc2
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/merger/ResultMergerTest.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.rpc.cluster.merger;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class ResultMergerTest {
+
+ @Test
+ public void testListMerger() throws Exception {
+ List<Object> list1 = new ArrayList<Object>();
+ list1.add( null );
+ list1.add( "1" );
+ list1.add( "2" );
+ List<Object> list2 = new ArrayList<Object>();
+ list2.add( "3" );
+ list2.add( "4" );
+
+ List result = MergerFactory.getMerger(List.class).merge(list1, list2);
+ Assert.assertEquals(5, result.size());
+ Assert.assertEquals( new ArrayList<String>(){
+ {
+ add( null );
+ add( "1" );
+ add( "2" );
+ add( "3" );
+ add( "4" );
+ }
+ }, result);
+ }
+
+ @Test
+ public void testSetMerger() throws Exception {
+ Set<Object> set1 = new HashSet<Object>();
+ set1.add( null );
+ set1.add( "1" );
+ set1.add( "2" );
+ Set<Object> set2 = new HashSet<Object>();
+ set2.add( "2" );
+ set2.add( "3" );
+
+ Set result = MergerFactory.getMerger(Set.class).merge(set1, set2);
+
+ Assert.assertEquals( 4, result.size() );
+ Assert.assertEquals( new HashSet<String>(){
+ {
+ add( null );
+ add( "1" );
+ add( "2" );
+ add( "3" );
+ }
+ }, result);
+ }
+
+ @Test
+ public void testArrayMerger() throws Exception {
+ String[] stringArray1 = {"1", "2", "3"};
+ String[] stringArray2 = {"4", "5", "6"};
+ String[] stringArray3 = {};
+
+ Object result = ArrayMerger.INSTANCE.merge(stringArray1, stringArray2, stringArray3);
+ Assert.assertTrue(result.getClass().isArray());
+ Assert.assertEquals(6, Array.getLength(result));
+ Assert.assertTrue(String.class.isInstance(Array.get(result, 0)));
+ for(int i = 0; i < 6; i++) {
+ Assert.assertEquals(String.valueOf(i + 1), Array.get(result, i));
+ }
+
+ int[] intArray1 = {1, 2, 3};
+ int[] intArray2 = {4, 5, 6};
+ int[] intArray3 = {7};
+ result = MergerFactory.getMerger(int[].class).merge(intArray1, intArray2, intArray3);
+ Assert.assertTrue(result.getClass().isArray());
+ Assert.assertEquals(7, Array.getLength(result));
+ Assert.assertTrue(int.class == result.getClass().getComponentType());
+ for (int i = 0; i < 7; i++) {
+ Assert.assertEquals(i + 1, Array.get(result, i));
+ }
+
+ }
+}
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/condition/ConditionRouterTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java
new file mode 100644
index 0000000..d492f6f
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.condition;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.router.MockInvoker;
+
+public class ConditionRouterTest {
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ private URL SCRIPT_URL = URL.valueOf("condition://0.0.0.0/com.foo.BarService");
+
+ private URL getRouteUrl(String rule) {
+ return SCRIPT_URL.addParameterAndEncoded(Constants.RULE_KEY, rule);
+ }
+
+ @Test
+ public void testRoute_ReturnFalse(){
+ Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + NetUtils.getLocalHost() + " => false"));
+ 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, URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation());
+ Assert.assertEquals(0, fileredInvokers.size());
+ }
+
+ @Test
+ public void testRoute_ReturnEmpty(){
+ Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + NetUtils.getLocalHost() + " => "));
+ 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, URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation());
+ Assert.assertEquals(0, fileredInvokers.size());
+ }
+
+ @Test
+ public void testRoute_ReturnAll(){
+ Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + NetUtils.getLocalHost() + " => " + " host = " + NetUtils.getLocalHost()));
+ 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, URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation());
+ Assert.assertEquals(invokers, fileredInvokers);
+ }
+
+ @Test
+ public void testRoute_HostFilter(){
+ Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + NetUtils.getLocalHost() + " => " + " host = " + NetUtils.getLocalHost()));
+ List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+ Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService")) ;
+ Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ List<Invoker<String>> fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation());
+ Assert.assertEquals(2, fileredInvokers.size());
+ Assert.assertEquals(invoker2, fileredInvokers.get(0));
+ Assert.assertEquals(invoker3, fileredInvokers.get(1));
+ }
+
+ @Test
+ public void testRoute_Empty_HostFilter(){
+ Router router = new ConditionRouterFactory().getRouter(getRouteUrl(" => " + " host = " + NetUtils.getLocalHost()));
+ List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+ Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService")) ;
+ Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ List<Invoker<String>> fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation());
+ Assert.assertEquals(2, fileredInvokers.size());
+ Assert.assertEquals(invoker2, fileredInvokers.get(0));
+ Assert.assertEquals(invoker3, fileredInvokers.get(1));
+ }
+
+ @Test
+ public void testRoute_False_HostFilter(){
+ Router router = new ConditionRouterFactory().getRouter(getRouteUrl("true => " + " host = " + NetUtils.getLocalHost()));
+ List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+ Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService")) ;
+ Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ List<Invoker<String>> fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation());
+ Assert.assertEquals(2, fileredInvokers.size());
+ Assert.assertEquals(invoker2, fileredInvokers.get(0));
+ Assert.assertEquals(invoker3, fileredInvokers.get(1));
+ }
+
+ @Test
+ public void testRoute_Placeholder(){
+ Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + NetUtils.getLocalHost() + " => " + " host = $host"));
+ List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+ Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService")) ;
+ Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ List<Invoker<String>> fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation());
+ Assert.assertEquals(2, fileredInvokers.size());
+ Assert.assertEquals(invoker2, fileredInvokers.get(0));
+ Assert.assertEquals(invoker3, fileredInvokers.get(1));
+ }
+
+ @Test
+ public void testRoute_NoForce(){
+ Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + NetUtils.getLocalHost() + " => " + " host = 1.2.3.4"));
+ List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+ Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService")) ;
+ Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ List<Invoker<String>> fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation());
+ Assert.assertEquals(invokers, fileredInvokers);
+ }
+
+ @Test
+ public void testRoute_Force(){
+ Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + NetUtils.getLocalHost() + " => " + " host = 1.2.3.4").addParameter(Constants.FORCE_KEY, String.valueOf(true)));
+ List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+ Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService")) ;
+ Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")) ;
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ List<Invoker<String>> fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation());
+ Assert.assertEquals(0, fileredInvokers.size());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
new file mode 100644
index 0000000..b376c7c
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router.file;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.script.ScriptEngineManager;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+
+/**
+ * @author chao.liuc
+ */
+@SuppressWarnings("unchecked")
+public class FileRouterEngineTest {
+ List<Invoker<FileRouterEngineTest>> invokers = new ArrayList<Invoker<FileRouterEngineTest>>();
+
+ Invoker<FileRouterEngineTest> invoker1 = EasyMock.createMock(Invoker.class);
+ Invoker<FileRouterEngineTest> invoker2 = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory<FileRouterEngineTest> dic;
+ Result result = new RpcResult();
+ private RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ private static boolean isScriptUnsupported = new ScriptEngineManager().getEngineByName("javascript") == null;
+
+ @Before
+ public void setUp() throws Exception {
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ }
+
+ @Test
+ public void testRouteNotAvailable() {
+ if (isScriptUnsupported) return;
+ URL url = initUrl("notAvailablerule.javascript");
+ initInvocation("method1");
+ initDic(url);
+ initInvokers(url, true, false);
+
+ MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker2, invoker);
+ }
+ }
+
+ @Test
+ public void testRouteAvailable() {
+ if (isScriptUnsupported) return;
+ URL url = initUrl("availablerule.javascript");
+ initInvocation("method1");
+ initDic(url);
+ initInvokers(url);
+
+ MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker1, invoker);
+ }
+ }
+
+ @Test
+ public void testRouteByMethodName() {
+ if (isScriptUnsupported) return;
+ URL url = initUrl("methodrule.javascript");
+ {
+ initInvocation("method1");
+ initDic(url);
+ initInvokers(url, true, true);
+
+ MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker1, invoker);
+ }
+ }
+ {
+ initInvocation("method2");
+ initDic(url);
+ initInvokers(url, true, true);
+ MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker2, invoker);
+ }
+ }
+ }
+
+ private URL initUrl(String filename) {
+ filename = getClass().getClassLoader().getResource(getClass().getPackage().getName().replace('.', '/') + "/" + filename).toString();
+ URL url = URL.valueOf(filename);
+ return url;
+ }
+
+ private void initInvocation(String methodName) {
+ invocation = new RpcInvocation();
+ ((RpcInvocation)invocation).setMethodName(methodName);
+ }
+
+ private void initInvokers(URL url) {
+ initInvokers(url, true, false);
+ }
+
+ private void initInvokers(URL url, boolean invoker1Status, boolean invoker2Status) {
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(invoker1Status).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(FileRouterEngineTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(invoker2Status).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(FileRouterEngineTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+ }
+
+ private void initDic(URL url) {
+ dic = new StaticDirectory<FileRouterEngineTest>(url, invokers, Arrays.asList(routerFactory.getRouter(url)));
+ }
+
+ static class MockClusterInvoker<T> extends AbstractClusterInvoker<T> {
+ private Invoker<T> selectedInvoker;
+
+ public MockClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ public MockClusterInvoker(Directory<T> directory, URL url) {
+ super(directory, url);
+ }
+
+ @Override
+ protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
+ LoadBalance loadbalance) throws RpcException {
+ Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+ selectedInvoker = invoker;
+ return null;
+ }
+
+ public Invoker<T> getSelectedInvoker() {
+ return selectedInvoker;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouterTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouterTest.java
new file mode 100644
index 0000000..e0a795a
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouterTest.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.cluster.router.script;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.router.MockInvoker;
+import com.alibaba.dubbo.rpc.cluster.router.script.ScriptRouterFactory;
+
+public class ScriptRouterTest {
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ private URL SCRIPT_URL = URL.valueOf("script://javascript?type=javascript");
+
+ private URL getRouteUrl(String rule) {
+ return SCRIPT_URL.addParameterAndEncoded(Constants.RULE_KEY, rule);
+ }
+
+ @Test
+ public void testRoute_ReturnAll(){
+ Router router = new ScriptRouterFactory().getRouter(getRouteUrl("function route(op1,op2){return op1} route(invokers)"));
+ List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+ invokers.add(new MockInvoker<String>());
+ invokers.add(new MockInvoker<String>());
+ invokers.add(new MockInvoker<String>());
+ List<Invoker<String>> fileredInvokers = router.route(invokers, invokers.get(0).getUrl(), 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, invokers.get(0).getUrl(), 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/support/AbstractClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
new file mode 100644
index 0000000..ed3a3ee
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
+import com.alibaba.dubbo.rpc.cluster.filter.DemoService;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;
+
+/**
+ * AbstractClusterInvokerTest
+ * @author chao.liuc
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class AbstractClusterInvokerTest {
+ List<Invoker<IHelloService>> invokers = new ArrayList<Invoker<IHelloService>>();
+ List<Invoker<IHelloService>> selectedInvokers = new ArrayList<Invoker<IHelloService>>();
+ AbstractClusterInvoker<IHelloService> cluster;
+ AbstractClusterInvoker<IHelloService> cluster_nocheck;
+ Directory<IHelloService> dic ;
+ RpcInvocation invocation = new RpcInvocation();
+ URL url = URL.valueOf("registry://localhost:9090");
+
+ Invoker<IHelloService> invoker1 ;
+ Invoker<IHelloService> invoker2 ;
+ Invoker<IHelloService> invoker3 ;
+ Invoker<IHelloService> invoker4 ;
+ Invoker<IHelloService> invoker5 ;
+ Invoker<IHelloService> mockedInvoker1 ;
+
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+ @SuppressWarnings({ "unchecked" })
+ @Before
+ public void setUp() throws Exception {
+ invocation.setMethodName("sayHello");
+
+ invoker1 = EasyMock.createMock(Invoker.class);
+ invoker2 = EasyMock.createMock(Invoker.class);
+ invoker3 = EasyMock.createMock(Invoker.class);
+ invoker4 = EasyMock.createMock(Invoker.class);
+ invoker5 = EasyMock.createMock(Invoker.class);
+ mockedInvoker1 = EasyMock.createMock(Invoker.class);
+
+ URL turl = URL.valueOf("test://test:11/test");
+
+ EasyMock.expect(invoker1.isAvailable()).andReturn(false).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(IHelloService.class).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(turl.addParameter("name", "invoker1")).anyTimes();
+
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(IHelloService.class).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(turl.addParameter("name", "invoker2")).anyTimes();
+
+ EasyMock.expect(invoker3.isAvailable()).andReturn(false).anyTimes();
+ EasyMock.expect(invoker3.getInterface()).andReturn(IHelloService.class).anyTimes();
+ EasyMock.expect(invoker3.getUrl()).andReturn(turl.addParameter("name", "invoker3")).anyTimes();
+
+ EasyMock.expect(invoker4.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker4.getInterface()).andReturn(IHelloService.class).anyTimes();
+ EasyMock.expect(invoker4.getUrl()).andReturn(turl.addParameter("name", "invoker4")).anyTimes();
+
+ EasyMock.expect(invoker5.isAvailable()).andReturn(false).anyTimes();
+ EasyMock.expect(invoker5.getInterface()).andReturn(IHelloService.class).anyTimes();
+ EasyMock.expect(invoker5.getUrl()).andReturn(turl.addParameter("name", "invoker5")).anyTimes();
+
+ EasyMock.expect(mockedInvoker1.isAvailable()).andReturn(false).anyTimes();
+ EasyMock.expect(mockedInvoker1.getInterface()).andReturn(IHelloService.class).anyTimes();
+ EasyMock.expect(mockedInvoker1.getUrl()).andReturn(turl.setProtocol("mock")).anyTimes();
+
+ EasyMock.replay(invoker1,invoker2,invoker3,invoker4,invoker5,mockedInvoker1);
+
+ invokers.add(invoker1);
+ dic = new StaticDirectory<IHelloService>(url, invokers, null);
+ cluster = new AbstractClusterInvoker(dic) {
+ @Override
+ protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance)
+ throws RpcException {
+ return null;
+ }
+ };
+
+ cluster_nocheck = new AbstractClusterInvoker(dic,url.addParameterIfAbsent(Constants.CLUSTER_AVAILABLE_CHECK_KEY, Boolean.FALSE.toString())) {
+ @Override
+ protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance)
+ throws RpcException {
+ return null;
+ }
+ };
+
+ }
+
+ @Test
+ public void testSelect_Invokersize0() throws Exception {
+ {
+ Invoker invoker = cluster.select(null,null,null,null);
+ Assert.assertEquals(null, invoker);
+ }
+ {
+ invokers.clear();
+ selectedInvokers.clear();
+ Invoker invoker = cluster.select(null,null,invokers,null);
+ Assert.assertEquals(null, invoker);
+ }
+ }
+
+ @Test
+ public void testSelect_Invokersize1() throws Exception {
+ invokers.clear();
+ invokers.add(invoker1);
+ Invoker invoker = cluster.select(null,null,invokers,null);
+ Assert.assertEquals(invoker1, invoker);
+ }
+
+ @Test
+ public void testSelect_Invokersize2AndselectNotNull() throws Exception {
+ invokers.clear();
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ {
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ Invoker invoker = cluster.select(null,null,invokers,selectedInvokers);
+ Assert.assertEquals(invoker2, invoker);
+ }
+ {
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ Invoker invoker = cluster.select(null,null,invokers,selectedInvokers);
+ Assert.assertEquals(invoker1, invoker);
+ }
+ }
+
+ @Test
+ public void testSelect_multiInvokers() throws Exception {
+ testSelect_multiInvokers( RoundRobinLoadBalance.NAME);
+ testSelect_multiInvokers( LeastActiveLoadBalance.NAME);
+ testSelect_multiInvokers( RandomLoadBalance.NAME);
+ }
+
+ @Test
+ public void testCloseAvailablecheck(){
+ LoadBalance lb = EasyMock.createMock(LoadBalance.class);
+ EasyMock.expect(lb.select(invokers, url, invocation)).andReturn(invoker1);
+ EasyMock.replay(lb);
+ initlistsize5();
+
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(false,sinvoker.isAvailable());
+ Assert.assertEquals(invoker1,sinvoker);
+
+ }
+
+ @Test
+ public void testDonotSelectAgainAndNoCheckAvailable(){
+
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME);
+ initlistsize5();
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertSame(invoker1, sinvoker);
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertSame(invoker2, sinvoker);
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers );
+ Assert.assertSame(invoker3, sinvoker);
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers );
+ Assert.assertSame(invoker5, sinvoker);
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(invokers.contains(sinvoker));
+ }
+
+ }
+
+ @Test
+ public void testSelectAgainAndCheckAvailable(){
+
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME);
+ initlistsize5();
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker4 );
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+ }
+ {
+ //边界测试.
+ for(int i=0;i<100;i++){
+ selectedInvokers.clear();
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+ }
+ }
+ {
+ //边界测试.
+ for(int i=0;i<100;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+ }
+ }
+ {
+ //边界测试.
+ for(int i=0;i<100;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+ }
+ }
+ }
+
+
+ public void testSelect_multiInvokers(String lbname) throws Exception {
+
+ int min=1000,max=5000;
+ Double d = (Math.random()*(max-min+1)+min);
+ int runs = d.intValue();
+ Assert.assertTrue(runs>min);
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(lbname);
+ initlistsize5();
+ for(int i=0;i<runs;i++){
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker4);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ }
+
+ /**
+ * 测试均衡.
+ */
+ @Test
+ public void testSelectBalance(){
+
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME);
+ initlistsize5();
+
+ Map<Invoker,AtomicLong> counter = new ConcurrentHashMap<Invoker,AtomicLong>();
+ for(Invoker invoker :invokers){
+ counter.put(invoker, new AtomicLong(0));
+ }
+ int runs = 1000;
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ counter.get(sinvoker).incrementAndGet();
+ }
+
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+// System.out.println(count);
+ if(minvoker.isAvailable())
+ Assert.assertTrue("count should > avg", count>runs/invokers.size());
+ }
+
+ Assert.assertEquals(runs, counter.get(invoker2).get()+counter.get(invoker4).get());;
+
+ }
+
+ private void initlistsize5(){
+ invokers.clear();
+ selectedInvokers.clear();//需要清除,之前的测试中会主动将正确的invoker2放入其中.
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ invokers.add(invoker4);
+ invokers.add(invoker5);
+ }
+ @Test()
+ public void testTimeoutExceptionCode() {
+ List<Invoker<DemoService>> invokers = new ArrayList<Invoker<DemoService>>();
+ invokers.add(new Invoker<DemoService>() {
+
+ public Class<DemoService> getInterface() {
+ return DemoService.class;
+ }
+
+ public URL getUrl() {
+ return URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/" + DemoService.class.getName());
+ }
+
+ public boolean isAvailable() {
+ return false;
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "test timeout");
+ }
+
+ public void destroy() {
+ }
+ });
+ Directory<DemoService> directory = new StaticDirectory<DemoService>(invokers);
+ FailoverClusterInvoker<DemoService> failoverClusterInvoker = new FailoverClusterInvoker<DemoService>(directory);
+ try {
+ failoverClusterInvoker.invoke(new RpcInvocation("sayHello", new Class<?>[0], new Object[0]));
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode());
+ }
+ ForkingClusterInvoker<DemoService> forkingClusterInvoker = new ForkingClusterInvoker<DemoService>(directory);
+ try {
+ forkingClusterInvoker.invoke(new RpcInvocation("sayHello", new Class<?>[0], new Object[0]));
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode());
+ }
+ FailfastClusterInvoker<DemoService> failfastClusterInvoker = new FailfastClusterInvoker<DemoService>(directory);
+ try {
+ failfastClusterInvoker.invoke(new RpcInvocation("sayHello", new Class<?>[0], new Object[0]));
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode());
+ }
+ }
+ /**
+ * 测试mock invoker选择是否正常
+ */
+ @Test
+ public void testMockedInvokerSelect() {
+ initlistsize5();
+ invokers.add(mockedInvoker1);
+
+ RpcInvocation mockedInvocation = new RpcInvocation();
+ mockedInvocation.setMethodName("sayHello");
+ mockedInvocation.setAttachment(Constants.INVOCATION_NEED_MOCK, "true");
+ List<Invoker<IHelloService>> mockedInvokers = dic.list(mockedInvocation);
+ Assert.assertEquals(1, mockedInvokers.size());
+
+ List<Invoker<IHelloService>> invokers = dic.list(invocation);
+ Assert.assertEquals(5, invokers.size());
+ }
+
+ public static interface IHelloService{}
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java
new file mode 100644
index 0000000..5d96da7
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.assertTrue;
+
+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.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.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);
+ RpcInvocation invocation = new RpcInvocation();
+ Directory<DemoService> dic ;
+ Result result = new RpcResult();
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(DemoService.class).anyTimes();
+ invocation.setMethodName("method1");
+ EasyMock.replay(dic);
+
+ invokers.add(invoker);
+ }
+
+ @After
+ public void tearDown(){
+ EasyMock.verify(invoker,dic);
+
+ }
+ 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);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(DemoService.class).anyTimes();
+
+ invocation.setMethodName("method1");
+ EasyMock.replay(dic);
+
+ resetInvokerToNoException();
+
+ FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
+ LogUtil.start();
+ invoker.invoke(invocation);
+ assertTrue(LogUtil.findMessage("No provider") > 0);
+ 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..8ad9065
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.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.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.RpcInvocation;
+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);
+ RpcInvocation invocation = new RpcInvocation();
+ Directory<FailbackClusterInvokerTest> dic;
+ Result result = new RpcResult();
+
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();
+
+ invocation.setMethodName("method1");
+ EasyMock.replay(dic);
+
+ invokers.add(invoker);
+ }
+
+ @After
+ public void tearDown() {
+ EasyMock.verify(invoker, dic);
+
+ }
+
+ 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);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();
+
+ invocation.setMethodName("method1");
+ EasyMock.replay(dic);
+
+ 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..72fdb8a
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.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.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.RpcInvocation;
+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);
+ RpcInvocation invocation = new RpcInvocation();
+ Directory<FailfastClusterInvokerTest> dic ;
+ Result result = new RpcResult();
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();
+
+ invocation.setMethodName("method1");
+ EasyMock.replay(dic);
+
+ invokers.add(invoker1);
+ }
+
+ @After
+ public void tearDown(){
+ EasyMock.verify(invoker1,dic);
+
+ }
+ 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);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();
+
+ invocation.setMethodName("method1");
+ EasyMock.replay(dic);
+
+ 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..f65de85
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+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.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+
+/**
+ * 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);
+ RpcInvocation invocation = new RpcInvocation();
+ Directory<FailoverClusterInvokerTest> dic ;
+ Result result = new RpcResult();
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+ invocation.setMethodName("method1");
+ EasyMock.replay(dic);
+
+ 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);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+ invocation.setMethodName("method1");
+ EasyMock.replay(dic);
+
+ invokers.add(invoker1);
+
+
+ FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
+ try {
+ invoker.invoke(invocation);
+ fail();
+ } catch (RpcException expected) {
+ assertFalse(expected.getCause() instanceof RpcException);
+ }
+ }
+
+ /**
+ * 测试在调用重试过程中,directory列表变更,invoke重试时重新进行list选择
+ */
+ @Test
+ public void testInvokerDestoryAndReList(){
+ final URL url = URL.valueOf("test://localhost/"+ Demo.class.getName() + "?loadbalance=roundrobin&retries="+retries);
+ RpcException exception = new RpcException(RpcException.TIMEOUT_EXCEPTION);
+ MockInvoker<Demo> invoker1 = new MockInvoker<Demo>(Demo.class, url);
+ invoker1.setException(exception);
+
+ MockInvoker<Demo> invoker2 = new MockInvoker<Demo>(Demo.class, url);
+ invoker2.setException(exception);
+
+ final List<Invoker<Demo>> invokers = new ArrayList<Invoker<Demo>>();
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+
+ Callable<Object> callable = new Callable<Object>() {
+ public Object call() throws Exception {
+ //模拟invoker全部被destroy掉
+ for (Invoker<Demo> invoker:invokers){
+ invoker.destroy();
+ }
+ invokers.clear();
+ MockInvoker<Demo> invoker3 = new MockInvoker<Demo>(Demo.class, url);
+ invokers.add(invoker3);
+ return null;
+ }
+ };
+ invoker1.setCallable(callable);
+ invoker2.setCallable(callable);
+
+ RpcInvocation inv = new RpcInvocation();
+ inv.setMethodName("test");
+
+ Directory<Demo> dic = new MockDirectory<Demo>(url, invokers);
+
+ FailoverClusterInvoker<Demo> clusterinvoker = new FailoverClusterInvoker<Demo>(dic);
+ clusterinvoker.invoke(inv);
+ }
+
+ public static interface Demo{}
+
+ public static class MockInvoker<T> extends AbstractInvoker<T> {
+ URL url;
+ boolean available = true;
+ boolean destoryed = false;
+ Result result ;
+ RpcException exception;
+ Callable<?> callable;
+
+ public MockInvoker(Class<T> type, URL url) {
+ super(type, url);
+ }
+
+ public void setResult(Result result) {
+ this.result = result;
+ }
+ public void setException(RpcException exception) {
+ this.exception = exception;
+ }
+ public void setCallable(Callable<?> callable) {
+ this.callable = callable;
+ }
+
+ @Override
+ protected Result doInvoke(Invocation invocation) throws Throwable {
+ if (callable != null) {
+ try {
+ callable.call();
+ } catch (Exception e) {
+ throw new RpcException(e);
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ } else {
+ return result;
+ }
+ }
+ }
+
+ public class MockDirectory<T> extends StaticDirectory<T> {
+ public MockDirectory(URL url , List<Invoker<T>> invokers) {
+ super(url, invokers);
+ }
+ @Override
+ protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
+ return new ArrayList<Invoker<T>>(super.doList(invocation));
+ }
+ }
+}
\ 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..6acb413
--- /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.RpcInvocation;
+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);
+ RpcInvocation invocation = new RpcInvocation();
+ Directory<ForkingClusterInvokerTest> dic;
+ Result result = new RpcResult();
+
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+
+ invocation.setMethodName("method1");
+ EasyMock.replay(dic);
+
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+
+ }
+
+ @After
+ public void tearDown() {
+ EasyMock.verify(invoker1, dic);
+
+ }
+
+ private void resetInvokerToException() {
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+ EasyMock.reset(invoker3);
+ EasyMock.expect(invoker3.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker3.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker3);
+ }
+
+ private void resetInvokerToNoException() {
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+ EasyMock.reset(invoker3);
+ EasyMock.expect(invoker3.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker3.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker3);
+ }
+
+ @Test
+ public void testInvokeExceptoin() {
+ resetInvokerToException();
+ ForkingClusterInvoker<ForkingClusterInvokerTest> invoker = new ForkingClusterInvoker<ForkingClusterInvokerTest>(
+ dic);
+
+ try {
+ invoker.invoke(invocation);
+ Assert.fail();
+ } catch (RpcException expected) {
+ Assert.assertTrue(expected.getMessage().contains("Failed to forking invoke provider"));
+ assertFalse(expected.getCause() instanceof RpcException);
+ }
+ }
+
+ @Test()
+ public void testInvokeNoExceptoin() {
+
+ resetInvokerToNoException();
+
+ ForkingClusterInvoker<ForkingClusterInvokerTest> invoker = new ForkingClusterInvoker<ForkingClusterInvokerTest>(
+ dic);
+ Result ret = invoker.invoke(invocation);
+ Assert.assertSame(result, ret);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/Menu.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/Menu.java
new file mode 100644
index 0000000..5788008
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/Menu.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class Menu {
+
+ private Map<String, List<String>> menus = new HashMap<String, List<String>>();
+
+ public Menu() {}
+
+ public Menu( Map<String, List<String>> menus ) {
+ this.menus.putAll( menus );
+ }
+
+ public void putMenuItem( String menu, String item ) {
+ List<String> items = menus.get( menu );
+ if ( item == null ) {
+ items = new ArrayList<String>();
+ menus.put( menu, items );
+ }
+ items.add( item );
+ }
+
+ public void addMenu( String menu, List<String> items ) {
+ List<String> menuItems = menus.get( menu );
+ if ( menuItems == null ) {
+ menus.put( menu, new ArrayList<String>( items ) );
+ } else {
+ menuItems.addAll( new ArrayList<String>( items ) );
+ }
+ }
+
+ public Map<String, List<String>> getMenus() {
+ return Collections.unmodifiableMap( menus );
+ }
+
+ public void merge( Menu menu ) {
+ for( Map.Entry<String, List<String>> entry : menu.menus.entrySet() ) {
+ addMenu( entry.getKey(), entry.getValue() );
+ }
+ }
+
+}
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MenuService.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MenuService.java
new file mode 100644
index 0000000..40368ca
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MenuService.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public interface MenuService {
+
+ public Menu getMenu();
+
+ public void addMenu( String menu, List<String> items );
+
+}
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java
new file mode 100644
index 0000000..47327e8
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.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.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MergeableClusterInvokerTest {
+
+ private Directory directory = EasyMock.createMock( Directory.class );
+ private Invoker firstInvoker = EasyMock.createMock( Invoker.class );
+ private Invoker secondInvoker = EasyMock.createMock( Invoker.class );
+ private Invocation invocation = EasyMock.createMock( Invocation.class );
+
+ private MergeableClusterInvoker<MenuService> mergeableClusterInvoker;
+
+ private Map<String, List<String>> firstMenuMap = new HashMap<String, List<String>>() {
+ {
+ put( "1", new ArrayList<String>() {
+ {
+ add( "10" );
+ add( "11" );
+ add( "12" );
+ }
+ } );
+ put( "2", new ArrayList<String>() {
+
+ {
+ add( "20" );
+ add( "21" );
+ add( "22" );
+ }
+ } );
+ }
+ };
+ private Map<String, List<String>> secondMenuMap = new HashMap<String, List<String>>() {
+ {
+ put( "2", new ArrayList<String>() {
+
+ {
+ add( "23" );
+ add( "24" );
+ add( "25" );
+ }
+ } );
+ put( "3", new ArrayList<String>() {
+
+ {
+ add( "30" );
+ add( "31" );
+ add( "32" );
+ }
+ } );
+ }
+ };
+
+ private Menu firstMenu = new Menu( firstMenuMap );
+ private Menu secondMenu = new Menu( secondMenuMap );
+
+ private URL url = URL.valueOf( new StringBuilder( 32 )
+ .append( "test://test/" )
+ .append( MenuService.class.getName() ).toString() );
+
+ @Before
+ public void setUp() throws Exception {
+
+ directory = EasyMock.createMock( Directory.class );
+ firstInvoker = EasyMock.createMock( Invoker.class );
+ secondInvoker = EasyMock.createMock( Invoker.class );
+ invocation = EasyMock.createMock( Invocation.class );
+
+ }
+
+ @Test
+ public void testGetMenuSuccessfully() throws Exception {
+
+ // setup
+ url = url.addParameter( Constants.MERGER_KEY, ".merge" );
+
+ EasyMock.expect( invocation.getMethodName() ).andReturn( "getMenu" ).anyTimes();
+ EasyMock.expect( invocation.getParameterTypes() ).andReturn( new Class<?>[]{ } ).anyTimes();
+ EasyMock.expect( invocation.getArguments() ).andReturn( new Object[]{ } ).anyTimes();
+ EasyMock.expect( invocation.getAttachments() ).andReturn( new HashMap<String, String>() )
+ .anyTimes();
+ EasyMock.expect( invocation.getInvoker() ).andReturn( firstInvoker ).anyTimes();
+ EasyMock.replay( invocation );
+
+ firstInvoker = (Invoker)Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{Invoker.class}, new InvocationHandler() {
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if ("getUrl".equals(method.getName())) {
+ return url.addParameter(Constants.GROUP_KEY, "first");
+ }
+ if ("getInterface".equals(method.getName())) {
+ return MenuService.class;
+ }
+ if ("invoke".equals(method.getName())) {
+ return new RpcResult(firstMenu);
+ }
+ return null;
+ }
+ });
+
+ secondInvoker = (Invoker) Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{Invoker.class}, new InvocationHandler() {
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if ("getUrl".equals(method.getName())) {
+ return url.addParameter(Constants.GROUP_KEY, "second");
+ }
+ if ("getInterface".equals(method.getName())) {
+ return MenuService.class;
+ }
+ if ("invoke".equals(method.getName())) {
+ return new RpcResult(secondMenu);
+ }
+ return null;
+ }
+ });
+
+ EasyMock.expect( directory.list( invocation ) ).andReturn( new ArrayList() {
+
+ {
+ add( firstInvoker );
+ add( secondInvoker );
+ }
+ } ).anyTimes();
+ EasyMock.expect( directory.getUrl() ).andReturn( url ).anyTimes();
+ EasyMock.expect( directory.getInterface() ).andReturn( MenuService.class ).anyTimes();
+ EasyMock.replay( directory );
+
+ mergeableClusterInvoker = new MergeableClusterInvoker<MenuService>( directory );
+
+ // invoke
+ Result result = mergeableClusterInvoker.invoke( invocation );
+ Assert.assertTrue( result.getValue() instanceof Menu );
+ Menu menu = ( Menu ) result.getValue();
+ Map<String, List<String>> expected = new HashMap<String, List<String>>();
+ merge( expected, firstMenuMap );
+ merge( expected, secondMenuMap );
+ Assert.assertEquals( expected, menu.getMenus() );
+
+ }
+
+ @Test
+ public void testAddMenu() throws Exception {
+
+ String menu = "first";
+ List<String> menuItems = new ArrayList<String>(){
+ {
+ add( "1" );
+ add( "2" );
+ }
+ };
+
+ EasyMock.expect( invocation.getMethodName() ).andReturn( "addMenu" ).anyTimes();
+ EasyMock.expect( invocation.getParameterTypes() ).andReturn(
+ new Class<?>[]{ String.class, List.class } ).anyTimes();
+ EasyMock.expect( invocation.getArguments() ).andReturn( new Object[]{ menu, menuItems } )
+ .anyTimes();
+ EasyMock.expect( invocation.getAttachments() ).andReturn( new HashMap<String, String>() )
+ .anyTimes();
+ EasyMock.expect( invocation.getInvoker() ).andReturn( firstInvoker ).anyTimes();
+ EasyMock.replay( invocation );
+
+ EasyMock.expect( firstInvoker.getUrl() ).andReturn(
+ url.addParameter( Constants.GROUP_KEY, "first" ) ).anyTimes();
+ EasyMock.expect( firstInvoker.getInterface() ).andReturn( MenuService.class ).anyTimes();
+ EasyMock.expect( firstInvoker.invoke(invocation) ).andReturn( new RpcResult() )
+ .anyTimes();
+ EasyMock.expect(firstInvoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.replay( firstInvoker );
+
+ EasyMock.expect( secondInvoker.getUrl() ).andReturn(
+ url.addParameter( Constants.GROUP_KEY, "second" ) ).anyTimes();
+ EasyMock.expect( secondInvoker.getInterface() ).andReturn( MenuService.class ).anyTimes();
+ EasyMock.expect( secondInvoker.invoke(invocation) ).andReturn(new RpcResult() )
+ .anyTimes();
+ EasyMock.expect(secondInvoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.replay( secondInvoker );
+
+ EasyMock.expect( directory.list( invocation ) ).andReturn( new ArrayList() {
+
+ {
+ add( firstInvoker );
+ add( secondInvoker );
+ }
+ } ).anyTimes();
+ EasyMock.expect( directory.getUrl() ).andReturn( url ).anyTimes();
+ EasyMock.expect( directory.getInterface() ).andReturn( MenuService.class ).anyTimes();
+ EasyMock.replay( directory );
+
+ mergeableClusterInvoker = new MergeableClusterInvoker<MenuService>( directory );
+
+ Result result = mergeableClusterInvoker.invoke( invocation );
+ Assert.assertNull( result.getValue() );
+
+ }
+
+ static void merge( Map<String, List<String>> first, Map<String, List<String>> second ) {
+ for( Map.Entry<String, List<String>> entry : second.entrySet() ) {
+ List<String> value = first.get( entry.getKey() );
+ if ( value != null ) {
+ value.addAll( entry.getValue() );
+ } else {
+ first.put( entry.getKey(), entry.getValue() );
+ }
+ }
+ }
+
+}
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
new file mode 100644
index 0000000..3cb7a02
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
@@ -0,0 +1,753 @@
+package com.alibaba.dubbo.rpc.cluster.support.wrapper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+import com.alibaba.dubbo.rpc.support.MockProtocol;
+
+public class MockClusterInvokerTest {
+
+ List<Invoker<IHelloService>> invokers = new ArrayList<Invoker<IHelloService>>();
+
+ @Before
+ public void beforeMethod(){
+ invokers.clear();
+ }
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerInvoke_normal(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName());
+ url = url.addParameter(Constants.MOCK_KEY, "fail" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ URL mockUrl = URL.valueOf("mock://localhost/"+IHelloService.class.getName()
+ +"?getSomething.mock=return aa");
+
+ Protocol protocol = new MockProtocol();
+ Invoker<IHelloService> mInvoker1 = protocol.refer(IHelloService.class, mockUrl);
+ invokers.add(mInvoker1);
+
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("something", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("sayHello");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals(null, ret.getValue());
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerInvoke_failmock(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter(Constants.MOCK_KEY, "fail:return null" )
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ URL mockUrl = URL.valueOf("mock://localhost/"+IHelloService.class.getName()
+ +"?getSomething.mock=return aa").addParameters(url.getParameters());
+
+ Protocol protocol = new MockProtocol();
+ Invoker<IHelloService> mInvoker1 = protocol.refer(IHelloService.class, mockUrl);
+ invokers.add(mInvoker1);
+
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("aa", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething2");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals(null, ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("sayHello");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals(null, ret.getValue());
+ }
+
+
+ /**
+ * 测试mock策略是否正常-force-mork
+ */
+ @Test
+ public void testMockInvokerInvoke_forcemock(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName());
+ url = url.addParameter(Constants.MOCK_KEY, "force:return null" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ URL mockUrl = URL.valueOf("mock://localhost/"+IHelloService.class.getName()
+ +"?getSomething.mock=return aa&getSomething3xx.mock=return xx")
+ .addParameters(url.getParameters());
+
+ Protocol protocol = new MockProtocol();
+ Invoker<IHelloService> mInvoker1 = protocol.refer(IHelloService.class, mockUrl);
+ invokers.add(mInvoker1);
+
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("aa", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething2");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals(null, ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("sayHello");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals(null, ret.getValue());
+ }
+
+ @Test
+ public void testMockInvokerInvoke_forcemock_defaultreturn(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName());
+ url = url.addParameter(Constants.MOCK_KEY, "force" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ URL mockUrl = URL.valueOf("mock://localhost/"+IHelloService.class.getName()
+ +"?getSomething.mock=return aa&getSomething3xx.mock=return xx&sayHello.mock=return ")
+ .addParameters(url.getParameters());
+
+ Protocol protocol = new MockProtocol();
+ Invoker<IHelloService> mInvoker1 = protocol.refer(IHelloService.class, mockUrl);
+ invokers.add(mInvoker1);
+
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("sayHello");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals(null, ret.getValue());
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_Fock_someMethods(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getSomething.mock","fail:return x")
+ .addParameter("getSomething2.mock","force:return y");
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("something", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething2");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("y", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething3");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("something3", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("sayHello");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals(null, ret.getValue());
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_Fock_WithOutDefault(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getSomething.mock","fail:return x")
+ .addParameter("getSomething2.mock","force:return y")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("x", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething2");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("y", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething3");
+ try {
+ ret = cluster.invoke(invocation);
+ Assert.fail();
+ }catch (RpcException e) {
+
+ }
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_Fock_WithDefault(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("mock","fail:return null")
+ .addParameter("getSomething.mock","fail:return x")
+ .addParameter("getSomething2.mock","force:return y")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("x", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething2");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("y", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething3");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals(null, ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("sayHello");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals(null, ret.getValue());
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_Fock_WithFailDefault(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("mock","fail:return z")
+ .addParameter("getSomething.mock","fail:return x")
+ .addParameter("getSomething2.mock","force:return y")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("x", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething2");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("y", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething3");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("z", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("sayHello");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("z", ret.getValue());
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_Fock_WithForceDefault(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("mock","force:return z")
+ .addParameter("getSomething.mock","fail:return x")
+ .addParameter("getSomething2.mock","force:return y")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("x", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething2");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("y", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething3");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("z", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("sayHello");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("z", ret.getValue());
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_Fock_Default(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("mock","fail:return x")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("x", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething2");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("x", ret.getValue());
+
+ //如d
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("sayHello");
+ ret = cluster.invoke(invocation);
+ Assert.assertEquals("x", ret.getValue());
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_checkCompatible_return(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getSomething.mock","return x")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("x", ret.getValue());
+
+ //如果没有配置mock,则直接返回null
+ invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething3");
+ try{
+ ret = cluster.invoke(invocation);
+ Assert.fail("fail invoke");
+ }catch(RpcException e){
+
+ }
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("mock","true")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("somethingmock", ret.getValue());
+ }
+
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock2(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("mock","fail")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("somethingmock", ret.getValue());
+ }
+ /**
+ * 测试mock策略是否正常-fail-mock
+ */
+ @Test
+ public void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock3(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("mock","force");
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals("somethingmock", ret.getValue());
+ }
+
+ @Test
+ public void testMockInvokerFromOverride_Invoke_check_String(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getSomething.mock","force:return 1688")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getSomething");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertTrue("result type must be String but was : " + ret.getValue().getClass(), ret.getValue() instanceof String);
+ Assert.assertEquals("1688", (String)ret.getValue());
+ }
+
+ @Test
+ public void testMockInvokerFromOverride_Invoke_check_int(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getInt1.mock","force:return 1688")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getInt1");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertTrue("result type must be integer but was : " + ret.getValue().getClass(), ret.getValue() instanceof Integer);
+ Assert.assertEquals(new Integer(1688), (Integer)ret.getValue());
+ }
+
+ @Test
+ public void testMockInvokerFromOverride_Invoke_check_boolean(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getBoolean1.mock","force:return true")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getBoolean1");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertTrue("result type must be Boolean but was : " + ret.getValue().getClass(), ret.getValue() instanceof Boolean);
+ Assert.assertEquals(true, Boolean.parseBoolean(ret.getValue().toString()));
+ }
+
+ @Test
+ public void testMockInvokerFromOverride_Invoke_check_Boolean(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getBoolean2.mock","force:return true")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getBoolean2");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals(true, Boolean.parseBoolean(ret.getValue().toString()));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testMockInvokerFromOverride_Invoke_check_ListString_empty(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getListString.mock","force:return empty")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getListString");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals(0, ((List<String>)ret.getValue()).size());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testMockInvokerFromOverride_Invoke_check_ListString(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getListString.mock","force:return [\"hi\",\"hi2\"]")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getListString");
+ Result ret = cluster.invoke(invocation);
+ List<String> rl = (List<String>)ret.getValue();
+ Assert.assertEquals(2, rl.size());
+ Assert.assertEquals("hi", rl.get(0));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testMockInvokerFromOverride_Invoke_check_ListPojo_empty(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getUsers.mock","force:return empty")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getUsers");
+ Result ret = cluster.invoke(invocation);
+ Assert.assertEquals(0, ((List<User>)ret.getValue()).size());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testMockInvokerFromOverride_Invoke_check_ListPojo(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getUsers.mock","force:return [{id:1, name:\"hi1\"}, {id:2, name:\"hi2\"}]")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getUsers");
+ Result ret = cluster.invoke(invocation);
+ List<User> rl = (List<User>)ret.getValue();
+ System.out.println(rl);
+ Assert.assertEquals(2, rl.size());
+ Assert.assertEquals("hi1", ((User)rl.get(0)).getName());
+ }
+
+ @Test
+ public void testMockInvokerFromOverride_Invoke_check_ListPojo_error(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getUsers.mock","force:return [{id:x, name:\"hi1\"}]")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getUsers");
+ try{
+ cluster.invoke(invocation);
+ }catch (RpcException e) {
+ }
+ }
+
+ @Test
+ public void testMockInvokerFromOverride_Invoke_force_throw(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getBoolean2.mock","force:throw ")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getBoolean2");
+ try {
+ cluster.invoke(invocation);
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertFalse("not custem exception", e.isBiz());
+ }
+ }
+
+ @Test
+ public void testMockInvokerFromOverride_Invoke_force_throwCustemException() throws Throwable{
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getBoolean2.mock","force:throw com.alibaba.dubbo.rpc.cluster.support.wrapper.MyMockException")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getBoolean2");
+ try {
+ cluster.invoke(invocation).recreate();
+ Assert.fail();
+ } catch (MyMockException e) {
+
+ }
+ }
+
+ @Test
+ public void testMockInvokerFromOverride_Invoke_force_throwCustemExceptionNotFound(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("getBoolean2.mock","force:throw java.lang.RuntimeException2")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getBoolean2");
+ try {
+ cluster.invoke(invocation);
+ Assert.fail();
+ } catch (Exception e) {
+ Assert.assertTrue(e.getCause() instanceof IllegalStateException);
+ }
+ }
+
+ @Test
+ public void testMockInvokerFromOverride_Invoke_mock_false(){
+ URL url = URL.valueOf("remote://1.2.3.4/"+IHelloService.class.getName())
+ .addParameter("mock","false")
+ .addParameter("invoke_return_error", "true" );
+ Invoker<IHelloService> cluster = getClusterInvoker(url);
+ //方法配置了mock
+ RpcInvocation invocation = new RpcInvocation();
+ invocation.setMethodName("getBoolean2");
+ try {
+ cluster.invoke(invocation);
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.isTimeout());
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private Invoker<IHelloService> getClusterInvoker(URL url){
+ //javasssit方式对方法参数类型判断严格,如果invocation数据设置不全,调用会失败.
+ final URL durl = url.addParameter("proxy", "jdk");
+ invokers.clear();
+ ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("jdk");
+ Invoker<IHelloService> invoker1 = proxy.getInvoker(new HelloService(), IHelloService.class, durl);
+ invokers.add(invoker1);
+
+ Directory<IHelloService> dic = new StaticDirectory<IHelloService>(durl, invokers, null);
+ AbstractClusterInvoker<IHelloService> cluster = new AbstractClusterInvoker(dic) {
+ @Override
+ protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance)
+ throws RpcException {
+ if (durl.getParameter("invoke_return_error", false)){
+ throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "test rpc exception");
+ } else {
+ return ((Invoker<?>)invokers.get(0)).invoke(invocation);
+ }
+ }
+ };
+ return new MockClusterInvoker<IHelloService>(dic, cluster);
+ }
+
+ public static interface IHelloService{
+ String getSomething();
+ String getSomething2();
+ String getSomething3();
+ int getInt1();
+ boolean getBoolean1();
+ Boolean getBoolean2();
+ public List<String> getListString();
+ public List<User> getUsers();
+ void sayHello();
+ }
+ public static class HelloService implements IHelloService {
+ public String getSomething() {
+ return "something";
+ }
+ public String getSomething2() {
+ return "something2";
+ }
+ public String getSomething3() {
+ return "something3";
+ }
+ public int getInt1() {
+ return 1;
+ }
+ public boolean getBoolean1() {
+ return false;
+ }
+ public Boolean getBoolean2() {
+ return Boolean.FALSE;
+ }
+ public List<String> getListString() {
+ return Arrays.asList(new String[]{"Tom","Jerry"});
+ }
+ public List<User> getUsers() {
+ return Arrays.asList(new User[]{new User(1,"Tom"),new User(2,"Jerry")});
+ }
+ public void sayHello() {
+ System.out.println("hello prety");
+ }
+ }
+
+ public static class IHelloServiceMock implements IHelloService {
+ public IHelloServiceMock() {
+
+ }
+ public String getSomething() {
+ return "somethingmock";
+ }
+ public String getSomething2() {
+ return "something2mock";
+ }
+ public String getSomething3() {
+ return "something3mock";
+ }
+ public List<String> getListString() {
+ return Arrays.asList(new String[]{"Tommock","Jerrymock"});
+ }
+ public List<User> getUsers() {
+ return Arrays.asList(new User[]{new User(1,"Tommock"),new User(2,"Jerrymock")});
+ }
+ public int getInt1() {
+ return 1;
+ }
+ public boolean getBoolean1() {
+ return false;
+ }
+ public Boolean getBoolean2() {
+ return Boolean.FALSE;
+ }
+
+ public void sayHello() {
+ System.out.println("hello prety");
+ }
+ }
+
+ public static class User{
+ private int id;
+ private String name;
+
+ public User() {
+ }
+ public User(int id, String name) {
+ super();
+ this.id = id;
+ this.name = name;
+ }
+ public int getId() {
+ return id;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setId(int id) {
+ this.id = id;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ }
+}
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MyMockException.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MyMockException.java
new file mode 100644
index 0000000..e2aa147
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/wrapper/MyMockException.java
@@ -0,0 +1,12 @@
+package com.alibaba.dubbo.rpc.cluster.support.wrapper;
+
+
+public class MyMockException extends RuntimeException{
+
+ public MyMockException(String message) {
+ super(message);
+ }
+
+ private static final long serialVersionUID = 2851692379597990457L;
+
+}
diff --git a/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript
new file mode 100644
index 0000000..c76de32
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript
@@ -0,0 +1,11 @@
+function route(invokers,invocation,context){
+ var result = new java.util.ArrayList(invokers.size());
+
+ for (i=0;i<invokers.size(); i++){
+ if (invokers.get(i).isAvailable()) {
+ result.add(invokers.get(i)) ;
+ }
+ } ;
+ return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/methodrule.javascript b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/methodrule.javascript
new file mode 100644
index 0000000..77ff456
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/methodrule.javascript
@@ -0,0 +1,10 @@
+function route(invokers,invocation,context){
+ var result = new java.util.ArrayList();
+ if (invokers.size()>1 && invocation.getMethodName() .equals("method1")) {
+ result.add(invokers.get(0)) ;
+ } else {
+ result.add(invokers.get(1)) ;
+ }
+ return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript
new file mode 100644
index 0000000..1fcdcae
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript
@@ -0,0 +1,11 @@
+function route(invokers,invocation,context){
+ var result = new java.util.ArrayList(invokers.size());
+
+ for (i=0;i<invokers.size(); i++){
+ if (!invokers.get(i).isAvailable()) {
+ result.add(invokers.get(i)) ;
+ }
+ } ;
+ return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/log4j.xml b/dubbo-cluster/src/test/resources/log4j.xml
new file mode 100644
index 0000000..e2c0263
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+ <appender name="DUBBO" class="com.alibaba.dubbo.common.utils.DubboAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />
+ </layout>
+ </appender>
+ <root>
+ <level value="INFO" />
+ <appender-ref ref="DUBBO" />
+ </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml
new file mode 100644
index 0000000..b7efb2e
--- /dev/null
+++ b/dubbo-common/pom.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.
+-->
+<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.2.2</version>
+ </parent>
+ <artifactId>dubbo-common</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The common module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>hessian-lite</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>fastjson</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java
new file mode 100644
index 0000000..dd5ae14
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import java.util.regex.Pattern;
+
+/**
+ * Constants
+ *
+ * @author william.liangf
+ */
+public class Constants {
+
+ public static final String PROVIDER = "provider";
+
+ public static final String CONSUMER = "consumer";
+
+ public static final String REGISTER = "register";
+
+ public static final String UNREGISTER = "unregister";
+
+ public static final String SUBSCRIBE = "subscribe";
+
+ public static final String UNSUBSCRIBE = "unsubscribe";
+
+ public static final String CATEGORY_KEY = "category";
+
+ public static final String PROVIDERS_CATEGORY = "providers";
+
+ public static final String CONSUMERS_CATEGORY = "consumers";
+
+ public static final String ROUTERS_CATEGORY = "routers";
+
+ public static final String CONFIGURATORS_CATEGORY = "configurators";
+
+ public static final String DEFAULT_CATEGORY = PROVIDERS_CATEGORY;
+
+ public static final String ENABLED_KEY = "enabled";
+
+ public static final String DISABLED_KEY = "disabled";
+
+ public static final String VALIDATION_KEY = "validation";
+
+ public static final String CACHE_KEY = "cache";
+
+ public static final String DYNAMIC_KEY = "dynamic";
+
+ public static final String DUBBO_PROPERTIES_KEY = "dubbo.properties.file";
+
+ public static final String DEFAULT_DUBBO_PROPERTIES = "dubbo.properties";
+
+ public static final String SENT_KEY = "sent";
+
+ public static final boolean DEFAULT_SENT = false;
+
+ public static final String REGISTRY_PROTOCOL = "registry";
+
+ public static final String $INVOKE = "$invoke";
+
+ public static final String $ECHO = "$echo";
+
+ public static final int DEFAULT_IO_THREADS = Runtime.getRuntime()
+ .availableProcessors() + 1;
+
+ public static final String DEFAULT_PROXY = "javassist";
+
+ public static final int DEFAULT_PAYLOAD = 8 * 1024 * 1024; // 8M
+
+ public static final String DEFAULT_CLUSTER = "failover";
+
+ public static final String DEFAULT_DIRECTORY = "dubbo";
+
+ public static final String DEFAULT_LOADBALANCE = "random";
+
+ public static final String DEFAULT_PROTOCOL = "dubbo";
+
+ public static final String DEFAULT_EXCHANGER = "header";
+
+ public static final String DEFAULT_TRANSPORTER = "netty";
+
+ public static final String DEFAULT_REMOTING_SERVER = "netty";
+
+ public static final String DEFAULT_REMOTING_CLIENT = "netty";
+
+ public static final String DEFAULT_REMOTING_CODEC = "dubbo";
+
+ public static final String DEFAULT_REMOTING_SERIALIZATION = "hessian2";
+
+ public static final String DEFAULT_HTTP_SERVER = "servlet";
+
+ public static final String DEFAULT_HTTP_CLIENT = "jdk";
+
+ public static final String DEFAULT_HTTP_SERIALIZATION = "json";
+
+ public static final String DEFAULT_CHARSET = "UTF-8";
+
+ public static final int DEFAULT_WEIGHT = 100;
+
+ public static final int DEFAULT_FORKS = 2;
+
+ public static final String DEFAULT_THREAD_NAME = "Dubbo";
+
+ public static final int DEFAULT_THREADS = 200;
+
+ public static final int DEFAULT_QUEUES = 0;
+
+ public static final int DEFAULT_THREAD_ALIVE = 60 * 1000;
+
+ public static final int DEFAULT_CONNECTIONS = 0;
+
+ public static final int DEFAULT_ACCEPTS = 0;
+
+ public static final int DEFAULT_IDLE_TIMEOUT = 600 * 1000;
+
+ public static final int DEFAULT_HEARTBEAT = 60 * 1000;
+
+ public static final int DEFAULT_TIMEOUT = 1000;
+
+ public static final int DEFAULT_CONNECT_TIMEOUT = 3000;
+
+ public static final int DEFAULT_RETRIES = 2;
+
+ // default buffer size is 8k.
+ public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
+
+ public static final int MAX_BUFFER_SIZE = 16 * 1024;
+
+ public static final int MIN_BUFFER_SIZE = 1 * 1024;
+
+ public static final String REMOVE_VALUE_PREFIX = "-";
+
+ public static final String HIDE_KEY_PREFIX = ".";
+
+ public static final String DEFAULT_KEY_PREFIX = "default.";
+
+ public static final String DEFAULT_KEY = "default";
+
+ public static final String LOADBALANCE_KEY = "loadbalance";
+
+ // key for router type, for e.g., "script"/"file", corresponding to ScriptRouterFactory.NAME, FileRouterFactory.NAME
+ public static final String ROUTER_KEY = "router";
+
+ public static final String CLUSTER_KEY = "cluster";
+
+ public static final String REGISTRY_KEY = "registry";
+
+ public static final String MONITOR_KEY = "monitor";
+
+ public static final String SIDE_KEY = "side";
+
+ public static final String PROVIDER_SIDE = "provider";
+
+ public static final String CONSUMER_SIDE = "consumer";
+
+ public static final String DEFAULT_REGISTRY = "dubbo";
+
+ public static final String BACKUP_KEY = "backup";
+
+ public static final String DIRECTORY_KEY = "directory";
+
+ public static final String DEPRECATED_KEY = "deprecated";
+
+ public static final String ANYHOST_KEY = "anyhost";
+
+ public static final String ANYHOST_VALUE = "0.0.0.0";
+
+ public static final String LOCALHOST_KEY = "localhost";
+
+ public static final String LOCALHOST_VALUE = "127.0.0.1";
+
+ public static final String APPLICATION_KEY = "application";
+
+ public static final String LOCAL_KEY = "local";
+
+ public static final String STUB_KEY = "stub";
+
+ public static final String MOCK_KEY = "mock";
+
+ public static final String PROTOCOL_KEY = "protocol";
+
+ public static final String PROXY_KEY = "proxy";
+
+ public static final String WEIGHT_KEY = "weight";
+
+ public static final String FORKS_KEY = "forks";
+
+ public static final String DEFAULT_THREADPOOL = "fixed";
+
+ public static final String DEFAULT_CLIENT_THREADPOOL = "cached";
+
+ public static final String THREADPOOL_KEY = "threadpool";
+
+ public static final String THREAD_NAME_KEY = "threadname";
+
+ public static final String IO_THREADS_KEY = "iothreads";
+
+ public static final String THREADS_KEY = "threads";
+
+ public static final String QUEUES_KEY = "queues";
+
+ public static final String THREAD_ALIVE_KEY = "threadalive";
+
+ public static final String EXECUTES_KEY = "executes";
+
+ public static final String BUFFER_KEY = "buffer";
+
+ public static final String PAYLOAD_KEY = "payload";
+
+ public static final String REFERENCE_FILTER_KEY = "reference.filter";
+
+ public static final String INVOKER_LISTENER_KEY = "invoker.listener";
+
+ public static final String SERVICE_FILTER_KEY = "service.filter";
+
+ public static final String EXPORTER_LISTENER_KEY = "exporter.listener";
+
+ public static final String ACCESS_LOG_KEY = "accesslog";
+
+ public static final String ACTIVES_KEY = "actives";
+
+ public static final String CONNECTIONS_KEY = "connections";
+
+ public static final String ACCEPTS_KEY = "accepts";
+
+ public static final String IDLE_TIMEOUT_KEY = "idle.timeout";
+
+ public static final String HEARTBEAT_KEY = "heartbeat";
+
+ public static final String HEARTBEAT_TIMEOUT_KEY = "heartbeat.timeout";
+
+ public static final String CONNECT_TIMEOUT_KEY = "connect.timeout";
+
+ public static final String TIMEOUT_KEY = "timeout";
+
+ public static final String RETRIES_KEY = "retries";
+
+ public static final String PROMPT_KEY = "prompt";
+
+ public static final String DEFAULT_PROMPT = "dubbo>";
+
+ public static final String CODEC_KEY = "codec";
+ public static final String DOWNSTREAM_CODEC_KEY = "codec.downstream";
+
+ public static final String SERIALIZATION_KEY = "serialization";
+
+ public static final String EXCHANGER_KEY = "exchanger";
+
+ public static final String TRANSPORTER_KEY = "transporter";
+
+ public static final String SERVER_KEY = "server";
+
+ public static final String CLIENT_KEY = "client";
+
+ public static final String ASYNC_KEY = "async";
+
+ public static final String TOKEN_KEY = "token";
+
+ public static final String METHOD_KEY = "method";
+
+ 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 * 15;
+
+ public static final String PID_KEY = "pid";
+
+ public static final String TIMESTAMP_KEY = "timestamp";
+
+ public static final String CHECK_KEY = "check";
+
+ public static final String REGISTER_KEY = "register";
+
+ public static final String SUBSCRIBE_KEY = "subscribe";
+
+ 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 CLASSIFIER_KEY = "classifier";
+
+ public static final String VERSION_KEY = "version";
+
+ public static final String REVISION_KEY = "revision";
+
+ public static final String DUBBO_VERSION_KEY = "dubbo";
+
+ public static final String HESSIAN_VERSION_KEY = "hessian.version";
+
+ public static final String DISPATHER_KEY = "dispather";
+
+ public static final String CHANNEL_HANDLER_KEY = "channel.handler";
+
+ public static final String DEFAULT_CHANNEL_HANDLER = "default";
+
+ public static final String ANY_VALUE = "*";
+
+ public static final String COMMA_SEPARATOR = ",";
+
+ public static final Pattern COMMA_SPLIT_PATTERN = Pattern
+ .compile("\\s*[,]+\\s*");
+
+ public final static String PATH_SEPARATOR = "/";
+
+ public static final String REGISTRY_SEPARATOR = "|";
+
+ public static final Pattern REGISTRY_SPLIT_PATTERN = Pattern
+ .compile("\\s*[|;]+\\s*");
+
+ public static final String SEMICOLON_SEPARATOR = ";";
+
+ public static final Pattern SEMICOLON_SPLIT_PATTERN = Pattern
+ .compile("\\s*[;]+\\s*");
+
+ public static final String CONNECT_QUEUE_CAPACITY = "connect.queue.capacity";
+
+ public static final String CONNECT_QUEUE_WARNING_SIZE = "connect.queue.warning.size";
+
+ public static final int DEFAULT_CONNECT_QUEUE_WARNING_SIZE = 1000;
+
+ public static final String CHANNEL_ATTRIBUTE_READONLY_KEY = "channel.readonly";
+
+ public static final String CHANNEL_READONLYEVENT_SENT_KEY = "channel.readonly.sent";
+
+ public static final String CHANNEL_SEND_READONLYEVENT_KEY = "channel.readonly.send";
+
+ public static final String COUNT_PROTOCOL = "count";
+
+ public static final String TRACE_PROTOCOL = "trace";
+
+ public static final String EMPTY_PROTOCOL = "empty";
+
+ public static final String ADMIN_PROTOCOL = "admin";
+
+ public static final String PROVIDER_PROTOCOL = "provider";
+
+ public static final String CONSUMER_PROTOCOL = "consumer";
+
+ public static final String ROUTE_PROTOCOL = "route";
+
+ public static final String SCRIPT_PROTOCOL = "script";
+
+ public static final String CONDITION_PROTOCOL = "condition";
+
+ public static final String MOCK_PROTOCOL = "mock";
+
+ public static final String RETURN_PREFIX = "return ";
+
+ public static final String THROW_PREFIX = "throw";
+
+ public static final String FAIL_PREFIX = "fail:";
+
+ public static final String FORCE_PREFIX = "force:";
+
+ public static final String FORCE_KEY = "force";
+
+ public static final String MERGER_KEY = "merger";
+
+ public static final String TPS_MAX_KEY = "tps.max";
+
+ /**
+ * 集群时是否排除非available的invoker
+ */
+ public static final String CLUSTER_AVAILABLE_CHECK_KEY = "cluster.availablecheck";
+
+ /**
+ */
+ public static final boolean DEFAULT_CLUSTER_AVAILABLE_CHECK = true;
+
+ /**
+ * 集群时是否启用sticky策略
+ */
+ public static final String CLUSTER_STICKY_KEY = "sticky";
+
+ /**
+ * sticky默认值.
+ */
+ public static final boolean DEFAULT_CLUSTER_STICKY = false;
+
+ /**
+ * 创建client时,是否先要建立连接。
+ */
+ public static final String LAZY_CONNECT_KEY = "lazy";
+
+ /**
+ * lazy连接的初始状态是连接状态还是非连接状态?
+ */
+ public static final String LAZY_CONNECT_INITIAL_STATE_KEY = "connect.lazy.initial.state";
+
+ /**
+ * lazy连接的初始状态默认是连接状态.
+ */
+ public static final boolean DEFAULT_LAZY_CONNECT_INITIAL_STATE = true;
+
+ /**
+ * 注册中心是否同步存储文件,默认异步
+ */
+ public static final String REGISTRY_FILESAVE_SYNC_KEY = "save.file";
+
+ /**
+ * 注册中心失败事件重试事件
+ */
+ public static final String REGISTRY_RETRY_PERIOD_KEY = "retry.period";
+
+ /**
+ * 重试周期
+ */
+ public static final int DEFAULT_REGISTRY_RETRY_PERIOD = 5 * 1000;
+
+ /**
+ * 注册中心自动重连时间
+ */
+ public static final String REGISTRY_RECONNECT_PERIOD_KEY = "reconnect.period";
+
+ public static final int DEFAULT_REGISTRY_RECONNECT_PERIOD = 3 * 1000;
+
+ public static final String SESSION_TIMEOUT_KEY = "session";
+
+ public static final int DEFAULT_SESSION_TIMEOUT = 60 * 1000;
+
+ /**
+ * 注册中心导出URL参数的KEY
+ */
+ public static final String EXPORT_KEY = "export";
+
+ /**
+ * 注册中心引用URL参数的KEY
+ */
+ public static final String REFER_KEY = "refer";
+
+ /**
+ * callback inst id
+ */
+ public static final String CALLBACK_SERVICE_KEY = "callback.service.instid";
+
+ /**
+ * 每个客户端同一个接口 callback服务实例的限制
+ */
+ public static final String CALLBACK_INSTANCES_LIMIT_KEY = "callbacks";
+
+ /**
+ * 每个客户端同一个接口 callback服务实例的限制
+ */
+ public static final int DEFAULT_CALLBACK_INSTANCES = 1;
+
+ public static final String CALLBACK_SERVICE_PROXY_KEY = "callback.service.proxy";
+
+ public static final String IS_CALLBACK_SERVICE = "is_callback_service";
+
+ /**
+ * channel中callback的invokers
+ */
+ public static final String CHANNEL_CALLBACK_KEY = "channel.callback.invokers.key";
+
+ @Deprecated
+ public static final String SHUTDOWN_WAIT_SECONDS_KEY = "dubbo.service.shutdown.wait.seconds";
+
+ public static final String SHUTDOWN_WAIT_KEY = "dubbo.service.shutdown.wait";
+
+ public static final String IS_SERVER_KEY = "isserver";
+
+ /**
+ * 默认值毫秒,避免重新计算.
+ */
+ public static final int DEFAULT_SERVER_SHUTDOWN_TIMEOUT = 10000;
+
+ public static final String ON_CONNECT_KEY = "onconnect";
+
+ public static final String ON_DISCONNECT_KEY = "ondisconnect";
+
+ public static final String RETURN_KEY = "return";
+
+ public static final String ON_INVOKE_METHOD_KEY = "oninvoke.method";
+
+ public static final String ON_RETURN_METHOD_KEY = "onreturn.method";
+
+ public static final String ON_THROW_METHOD_KEY = "onthrow.method";
+
+ public static final String ON_INVOKE_INSTANCE_KEY = "oninvoke.instance";
+
+ public static final String ON_RETURN_INSTANCE_KEY = "onreturn.instance";
+
+ public static final String ON_THROW_INSTANCE_KEY = "onthrow.instance";
+
+ public static final String OVERRIDE_PROTOCOL = "override";
+
+ public static final String PRIORITY_KEY = "priority";
+
+ public static final String RULE_KEY = "rule";
+
+ public static final String TYPE_KEY = "type";
+
+ public static final String RUNTIME_KEY = "runtime";
+
+ // when ROUTER_KEY's value is set to ROUTER_TYPE_CLEAR, RegistryDirectory will clean all current routers
+ public static final String ROUTER_TYPE_CLEAR = "clean";
+
+ public static final String DEFAULT_SCRIPT_TYPE_KEY = "javascript";
+
+ public static final String STUB_EVENT_KEY = "dubbo.stub.event";
+
+ public static final boolean DEFAULT_STUB_EVENT = false;
+
+ public static final String STUB_EVENT_METHODS_KEY = "dubbo.stub.event.methods";
+
+ //invocation attachment属性中如果有此值,则选择mock invoker
+ public static final String INVOCATION_NEED_MOCK = "invocation.need.mock";
+
+ public static final String LOCAL_PROTOCOL = "injvm";
+
+ public static final String AUTO_ATTACH_INVOCATIONID_KEY = "invocationid.autoattach";
+
+ public static final String SCOPE_KEY = "scope";
+
+ public static final String SCOPE_LOCAL = "local";
+
+ public static final String SCOPE_REMOTE = "remote";
+
+ public static final String SCOPE_NONE = "none";
+
+ public static final String RELIABLE_PROTOCOL = "napoli";
+ /*
+ * private Constants(){ }
+ */
+
+ public static class Attachments {
+ public static final String IS_ASYNC_KEY = "attachments.async";
+ public static final String IS_ONEWAY_KEY = "attachments.oneway";
+ public static final String INVOCATIONID_KEY = "attachments.invocation.id";
+ }
+
+}
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..9c9b2ef
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Extension.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;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 替换:
+ * 请使用KV格式的配置文件:META-INF/dubbo/com.xxx.Protocol,文件内容:xxx=com.xxx.XxxProtocol
+ *
+ * 原因:
+ * 当扩展点的static字段或方法签名上引用了三方库,
+ * 如果三方库不存在,会导致类初始化失败,
+ * Extension标识Dubbo就拿不到了,异常信息就和配置对应不起来。
+ *
+ * 比如:
+ * Extension("mina")加载失败,
+ * 当用户配置使用mina时,就会报找不到扩展点,
+ * 而不是报加载扩展点失败,以及失败原因。
+ *
+ * @deprecated
+ * @author william.liangf
+ * @author ding.lid
+ */
+@Deprecated
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Extension {
+
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ String value() default "";
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Node.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Node.java
new file mode 100644
index 0000000..8884158
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Node.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Node. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public interface Node {
+
+ /**
+ * get url.
+ *
+ * @return url.
+ */
+ URL getUrl();
+
+ /**
+ * is available.
+ *
+ * @return available.
+ */
+ boolean isAvailable();
+
+ /**
+ * destroy.
+ */
+ void destroy();
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.java
new file mode 100644
index 0000000..ba907f0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+/**
+ * 兼容2.0.5之前版本
+ * @deprecated
+ * @author tony.chenl
+ */
+@Deprecated
+public class Parameters {
+ private final Map<String, String> parameters;
+
+ protected static final Logger logger = LoggerFactory.getLogger(Parameters.class);
+
+ public Parameters(String... pairs) {
+ this(toMap(pairs));
+ }
+
+ public Parameters(Map<String, String> parameters){
+ this.parameters = Collections.unmodifiableMap(parameters != null ? new HashMap<String, String>(parameters) : new HashMap<String, String>(0));
+ }
+
+ private static Map<String, String> toMap(String... pairs) {
+ Map<String, String> parameters = new HashMap<String, String>();
+ if (pairs.length > 0) {
+ if (pairs.length % 2 != 0) {
+ throw new IllegalArgumentException("pairs must be even.");
+ }
+ for (int i = 0; i < pairs.length; i = i + 2) {
+ parameters.put(pairs[i], pairs[i + 1]);
+ }
+ }
+ return parameters;
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public <T> T getExtension(Class<T> type, String key) {
+ String name = getParameter(key);
+ return ExtensionLoader.getExtensionLoader(type).getExtension(name);
+ }
+
+ public <T> T getExtension(Class<T> type, String key, String defaultValue) {
+ String name = getParameter(key, defaultValue);
+ return ExtensionLoader.getExtensionLoader(type).getExtension(name);
+ }
+
+ public <T> T getMethodExtension(Class<T> type, String method, String key) {
+ String name = getMethodParameter(method, key);
+ return ExtensionLoader.getExtensionLoader(type).getExtension(name);
+ }
+
+ public <T> T getMethodExtension(Class<T> type, String method, String key, String defaultValue) {
+ String name = getMethodParameter(method, key, defaultValue);
+ return ExtensionLoader.getExtensionLoader(type).getExtension(name);
+ }
+
+ public String getDecodedParameter(String key) {
+ return getDecodedParameter(key, null);
+ }
+
+ public String getDecodedParameter(String key, String defaultValue) {
+ String value = getParameter(key, defaultValue);
+ if (value != null && value.length() > 0) {
+ try {
+ value = URLDecoder.decode(value, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ return value;
+ }
+
+ public String getParameter(String key) {
+ String value = parameters.get(key);
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.HIDE_KEY_PREFIX + key);
+ }
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.DEFAULT_KEY_PREFIX + key);
+ }
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.HIDE_KEY_PREFIX + Constants.DEFAULT_KEY_PREFIX + key);
+ }
+ return value;
+ }
+
+ public String getParameter(String key, String defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public int getIntParameter(String key) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return 0;
+ }
+ return Integer.parseInt(value);
+ }
+
+ public int getIntParameter(String key, int defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Integer.parseInt(value);
+ }
+
+ public int getPositiveIntParameter(String key, int defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ int i = Integer.parseInt(value);
+ if (i > 0) {
+ return i;
+ }
+ return defaultValue;
+ }
+
+ public boolean getBooleanParameter(String key) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return false;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean getBooleanParameter(String key, boolean defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean hasParamter(String key) {
+ String value = getParameter(key);
+ return value != null && value.length() > 0;
+ }
+
+ public String getMethodParameter(String method, String key) {
+ String value = parameters.get(method + "." + key);
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.HIDE_KEY_PREFIX + method + "." + key);
+ }
+ if (value == null || value.length() == 0) {
+ return getParameter(key);
+ }
+ return value;
+ }
+
+ public String getMethodParameter(String method, String key, String defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public int getMethodIntParameter(String method, String key) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return 0;
+ }
+ return Integer.parseInt(value);
+ }
+
+ public int getMethodIntParameter(String method, String key, int defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Integer.parseInt(value);
+ }
+
+ public int getMethodPositiveIntParameter(String method, String key, int defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ int i = Integer.parseInt(value);
+ if (i > 0) {
+ return i;
+ }
+ return defaultValue;
+ }
+
+ public boolean getMethodBooleanParameter(String method, String key) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return false;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean hasMethodParamter(String method, String key) {
+ String value = getMethodParameter(method, key);
+ return value != null && value.length() > 0;
+ }
+
+ public static Parameters parseParameters(String query) {
+ return new Parameters(StringUtils.parseQueryString(query));
+ }
+
+ public boolean equals(Object o) {
+ return parameters.equals(o);
+ }
+
+ public int hashCode() {
+ return parameters.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return StringUtils.toQueryString(getParameters());
+ }
+
+ }
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Resetable.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Resetable.java
new file mode 100644
index 0000000..1ef0985
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Resetable.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Resetable.
+ *
+ * @author william.liangf
+ */
+public interface Resetable {
+
+ /**
+ * reset.
+ *
+ * @param url
+ */
+ void reset(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java
new file mode 100644
index 0000000..9f5c653
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java
@@ -0,0 +1,1309 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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&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&param2=value2
+ * </ul>
+ * <p>
+ * Some strange example below:
+ * <ul>
+ * <li>192.168.1.3:20880<br>
+ * for this case, url protocol = null, url host = 192.168.1.3, port = 20880, url path = null
+ * <li>file:///home/user1/router.js?type=script<br>
+ * for this case, url protocol = null, url host = null, url path = home/user1/router.js
+ * <li>file://home/user1/router.js?type=script<br>
+ * for this case, url protocol = file, url host = home, url path = user1/router.js
+ * <li>file:///D:/1/router.js?type=script<br>
+ * for this case, url protocol = file, url host = null, url path = D:/1/router.js
+ * <li>file:/D:/1/router.js?type=script<br>
+ * same as above file:///D:/1/router.js?type=script
+ * <li>/home/user1/router.js?type=script <br>
+ * for this case, url protocol = null, url host = null, url path = home/user1/router.js
+ * <li>home/user1/router.js?type=script <br>
+ * for this case, url protocol = null, url host = home, url path = user1/router.js
+ * </ul>
+ *
+ * @author william.liangf
+ * @author ding.lid
+ * @see java.net.URL
+ * @see java.net.URI
+ */
+public final class URL implements Serializable {
+
+ private static final long serialVersionUID = -1985165475234910535L;
+
+ private final String protocol;
+
+ private final String username;
+
+ private final String password;
+
+ private final String host;
+
+ private final int port;
+
+ private final String path;
+
+ private final Map<String, String> parameters;
+
+ // ==== cache ====
+
+ private volatile transient Map<String, Number> numbers;
+
+ private volatile transient Map<String, URL> urls;
+
+ private volatile transient String ip;
+
+ private volatile transient String full;
+
+ private volatile transient String identity;
+
+ private volatile transient String parameter;
+
+ private volatile transient String string;
+
+ protected URL() {
+ this.protocol = null;
+ this.username = null;
+ this.password = null;
+ this.host = null;
+ this.port = 0;
+ this.path = null;
+ this.parameters = null;
+ }
+
+ public URL(String protocol, String host, int port) {
+ this(protocol, null, null, host, port, null, (Map<String, String>) null);
+ }
+
+ public URL(String protocol, String host, int port, String[] pairs) { // 变长参数...与下面的path参数冲突,改为数组
+ 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;
+ 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);
+ }
+ 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 getAuthority() {
+ if ((username == null || username.length() == 0)
+ && (password == null || password.length() == 0)) {
+ return null;
+ }
+ return (username == null ? "" : username)
+ + ":" + (password == null ? "" : 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 int getPort(int defaultPort) {
+ return port == 0 ? defaultPort : port;
+ }
+
+ public String getAddress() {
+ return port <= 0 ? host : host + ":" + port;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public String getAbsolutePath() {
+ if (path != null && !path.startsWith("/")) {
+ return "/" + path;
+ }
+ return path;
+ }
+
+ public URL setProtocol(String protocol) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setUsername(String username) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setPassword(String password) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setAddress(String address) {
+ int i = address.lastIndexOf(':');
+ String host;
+ int port = this.port;
+ if (i >= 0) {
+ host = address.substring(0, i);
+ port = Integer.parseInt(address.substring(i + 1));
+ } else {
+ host = address;
+ }
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setHost(String host) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setPort(int port) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setPath(String path) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public String getParameterAndDecoded(String key) {
+ return getParameterAndDecoded(key, null);
+ }
+
+ public String getParameterAndDecoded(String key, String defaultValue) {
+ return decode(getParameter(key, defaultValue));
+ }
+
+ public String getParameter(String key) {
+ String value = parameters.get(key);
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.DEFAULT_KEY_PREFIX + key);
+ }
+ return value;
+ }
+
+ public String getParameter(String key, String defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public String[] getParameter(String key, String[] defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Constants.COMMA_SPLIT_PATTERN.split(value);
+ }
+
+ private Map<String, Number> getNumbers() {
+ if (numbers == null) { // 允许并发重复创建
+ numbers = new ConcurrentHashMap<String, Number>();
+ }
+ return numbers;
+ }
+
+ private Map<String, URL> getUrls() {
+ if (urls == null) { // 允许并发重复创建
+ urls = new ConcurrentHashMap<String, URL>();
+ }
+ return urls;
+ }
+
+ public URL getUrlParameter(String key) {
+ URL u = getUrls().get(key);
+ if (u != null) {
+ return u;
+ }
+ String value = getParameterAndDecoded(key);
+ if (value == null || value.length() == 0) {
+ return null;
+ }
+ u = URL.valueOf(value);
+ getUrls().put(key, u);
+ return u;
+ }
+
+ public double getParameter(String key, double defaultValue) {
+ Number n = getNumbers().get(key);
+ if (n != null) {
+ return n.doubleValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ double d = Double.parseDouble(value);
+ getNumbers().put(key, d);
+ return d;
+ }
+
+ public float getParameter(String key, float defaultValue) {
+ Number n = getNumbers().get(key);
+ if (n != null) {
+ return n.floatValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ float f = Float.parseFloat(value);
+ getNumbers().put(key, f);
+ return f;
+ }
+
+ public long getParameter(String key, long defaultValue) {
+ Number n = getNumbers().get(key);
+ if (n != null) {
+ return n.longValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ long l = Long.parseLong(value);
+ getNumbers().put(key, l);
+ return l;
+ }
+
+ public int getParameter(String key, int defaultValue) {
+ Number n = getNumbers().get(key);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ int i = Integer.parseInt(value);
+ getNumbers().put(key, i);
+ return i;
+ }
+
+ public short getParameter(String key, short defaultValue) {
+ Number n = getNumbers().get(key);
+ if (n != null) {
+ return n.shortValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ short s = Short.parseShort(value);
+ getNumbers().put(key, s);
+ return s;
+ }
+
+ public byte getParameter(String key, byte defaultValue) {
+ Number n = getNumbers().get(key);
+ if (n != null) {
+ return n.byteValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ byte b = Byte.parseByte(value);
+ getNumbers().put(key, b);
+ return b;
+ }
+
+ public float getPositiveParameter(String key, float defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ float value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public double getPositiveParameter(String key, double defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ double value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public long getPositiveParameter(String key, long defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ long value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public int getPositiveParameter(String key, int defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ int value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public short getPositiveParameter(String key, short defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ short value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public byte getPositiveParameter(String key, byte defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ byte value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public char getParameter(String key, char defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value.charAt(0);
+ }
+
+ public boolean getParameter(String key, boolean defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean hasParameter(String key) {
+ String value = getParameter(key);
+ return value != null && value.length() > 0;
+ }
+
+ public String getMethodParameterAndDecoded(String method, String key) {
+ return URL.decode(getMethodParameter(method, key));
+ }
+
+ public String getMethodParameterAndDecoded(String method, String key, String defaultValue) {
+ return URL.decode(getMethodParameter(method, key, defaultValue));
+ }
+
+ public String getMethodParameter(String method, String key) {
+ String value = parameters.get(method + "." + key);
+ if (value == null || value.length() == 0) {
+ return getParameter(key);
+ }
+ return value;
+ }
+
+ public String getMethodParameter(String method, String key, String defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public double getMethodParameter(String method, String key, double defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = getNumbers().get(methodKey);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ double d = Double.parseDouble(value);
+ getNumbers().put(methodKey, d);
+ return d;
+ }
+
+ public float getMethodParameter(String method, String key, float defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = getNumbers().get(methodKey);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ float f = Float.parseFloat(value);
+ getNumbers().put(methodKey, f);
+ return f;
+ }
+
+ public long getMethodParameter(String method, String key, long defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = getNumbers().get(methodKey);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ long l = Long.parseLong(value);
+ getNumbers().put(methodKey, l);
+ return l;
+ }
+
+ public int getMethodParameter(String method, String key, int defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = getNumbers().get(methodKey);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ int i = Integer.parseInt(value);
+ getNumbers().put(methodKey, i);
+ return i;
+ }
+
+ public short getMethodParameter(String method, String key, short defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = getNumbers().get(methodKey);
+ if (n != null) {
+ return n.shortValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ short s = Short.parseShort(value);
+ getNumbers().put(methodKey, s);
+ return s;
+ }
+
+ public byte getMethodParameter(String method, String key, byte defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = getNumbers().get(methodKey);
+ if (n != null) {
+ return n.byteValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ byte b = Byte.parseByte(value);
+ getNumbers().put(methodKey, b);
+ return b;
+ }
+
+ public double getMethodPositiveParameter(String method, String key, double defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ double value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public float getMethodPositiveParameter(String method, String key, float defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ float value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public long getMethodPositiveParameter(String method, String key, long defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ long value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public int getMethodPositiveParameter(String method, String key, int defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ int value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public short getMethodPositiveParameter(String method, String key, short defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ short value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public byte getMethodPositiveParameter(String method, String key, byte defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ byte value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public char getMethodParameter(String method, String key, char defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value.charAt(0);
+ }
+
+ public boolean getMethodParameter(String method, String key, boolean defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean hasMethodParameter(String method, String key) {
+ if (method == null) {
+ String suffix = "." + key;
+ for (String fullKey : parameters.keySet()) {
+ if (fullKey.endsWith(suffix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (key == null) {
+ String prefix = method + ".";
+ for (String fullKey : parameters.keySet()) {
+ if (fullKey.startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ String value = getMethodParameter(method, key);
+ return value != null && value.length() > 0;
+ }
+
+ public boolean isLocalHost() {
+ return NetUtils.isLocalHost(host) || getParameter(Constants.LOCALHOST_KEY, false);
+ }
+
+ public boolean isAnyHost() {
+ return Constants.ANYHOST_VALUE.equals(host) || getParameter(Constants.ANYHOST_KEY, false);
+ }
+
+ public URL addParameterAndEncoded(String key, String value) {
+ if(value == null || value.length() == 0) {
+ return this;
+ }
+ return addParameter(key, encode(value));
+ }
+
+ public URL addParameter(String key, boolean value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, char value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, byte value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, short value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, int value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, long value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, float value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, double value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, Enum<?> value) {
+ if(value == null) return this;
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, Number value) {
+ if(value == null) return this;
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, CharSequence value) {
+ if(value == null || value.length() == 0) return this;
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, String value) {
+ if (key == null || key.length() == 0
+ || value == null || value.length() == 0) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(getParameters());
+ map.put(key, value);
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ public URL addParameterIfAbsent(String key, String value) {
+ if (key == null || key.length() == 0
+ || value == null || value.length() == 0) {
+ return this;
+ }
+ if (hasParameter(key)) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(getParameters());
+ map.put(key, value);
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ /**
+ * Add parameters to a new url.
+ *
+ * @param parameters
+ * @return A new URL
+ */
+ public URL addParameters(Map<String, String> parameters) {
+ if (parameters == null || parameters.size() == 0) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(getParameters());
+ map.putAll(parameters);
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ public URL addParametersIfAbsent(Map<String, String> parameters) {
+ if (parameters == null || parameters.size() == 0) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(parameters);
+ map.putAll(getParameters());
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ public URL addParameters(String... pairs) {
+ if (pairs == null || pairs.length == 0) {
+ return this;
+ }
+ if (pairs.length % 2 != 0) {
+ throw new IllegalArgumentException("Map pairs can not be odd number.");
+ }
+ Map<String, String> map = new HashMap<String, String>();
+ int len = pairs.length / 2;
+ for (int i = 0; i < len; i ++) {
+ map.put(pairs[2 * i], pairs[2 * i + 1]);
+ }
+ return addParameters(map);
+ }
+
+ public URL addParameterString(String query) {
+ if (query == null || query.length() == 0) {
+ return this;
+ }
+ return addParameters(StringUtils.parseQueryString(query));
+ }
+
+ public URL removeParameter(String key) {
+ if (key == null || key.length() == 0) {
+ return this;
+ }
+ return removeParameters(key);
+ }
+
+ public URL removeParameters(Collection<String> keys) {
+ if (keys == null || keys.size() == 0) {
+ return this;
+ }
+ return removeParameters(keys.toArray(new String[0]));
+ }
+
+ public URL removeParameters(String... keys) {
+ if (keys == null || keys.length == 0) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(getParameters());
+ for (String key : keys) {
+ map.remove(key);
+ }
+ if (map.size() == getParameters().size()) {
+ return this;
+ }
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ public URL clearParameters() {
+ return new URL(protocol, username, password, host, port, path, new HashMap<String, String>());
+ }
+
+ public String getRawParameter(String key) {
+ if ("protocol".equals(key))
+ return protocol;
+ if ("username".equals(key))
+ return username;
+ if ("password".equals(key))
+ return password;
+ if ("host".equals(key))
+ return host;
+ if ("port".equals(key))
+ return String.valueOf(port);
+ if ("path".equals(key))
+ return path;
+ return getParameter(key);
+ }
+
+ public Map<String, String> toMap() {
+ Map<String, String> map = new HashMap<String, String>(parameters);
+ if (protocol != null)
+ map.put("protocol", protocol);
+ if (username != null)
+ map.put("username", username);
+ if (password != null)
+ map.put("password", password);
+ if (host != null)
+ map.put("host", host);
+ if (port > 0)
+ map.put("port", String.valueOf(port));
+ if (path != null)
+ map.put("path", path);
+ return map;
+ }
+
+ public String toString() {
+ if (string != null) {
+ return string;
+ }
+ return string = buildString(false, true); // no show username and password
+ }
+
+ public String toString(String... parameters) {
+ return buildString(false, true, parameters); // no show username and password
+ }
+
+ public String toIdentityString() {
+ if (identity != null) {
+ return identity;
+ }
+ return identity = buildString(true, false); // only return identity message, see the method "equals" and "hashCode"
+ }
+
+ public String toIdentityString(String... parameters) {
+ return buildString(true, false, parameters); // only return identity message, see the method "equals" and "hashCode"
+ }
+
+ public String toFullString() {
+ if (full != null) {
+ return full;
+ }
+ return full = buildString(true, true);
+ }
+
+ public String toFullString(String... parameters) {
+ return buildString(true, true, parameters);
+ }
+
+ public String toParameterString() {
+ if (parameter != null) {
+ return parameter;
+ }
+ return parameter = toParameterString(new String[0]);
+ }
+
+ public String toParameterString(String... parameters) {
+ StringBuilder buf = new StringBuilder();
+ buildParameters(buf, false, parameters);
+ return buf.toString();
+ }
+
+ private void buildParameters(StringBuilder buf, boolean concat, String[] parameters) {
+ if (getParameters() !=null && getParameters().size() > 0) {
+ List<String> includes = (parameters == null || parameters.length == 0 ? null : Arrays.asList(parameters));
+ boolean first = true;
+ for (Map.Entry<String, String> entry : new TreeMap<String, String>(getParameters()).entrySet()) {
+ if (entry.getKey() != null && entry.getKey().length() > 0
+ && (includes == null || includes.contains(entry.getKey()))) {
+ if (first) {
+ if (concat) {
+ buf.append("?");
+ }
+ first = false;
+ } else {
+ buf.append("&");
+ }
+ buf.append(entry.getKey());
+ buf.append("=");
+ buf.append(entry.getValue() == null ? "" : entry.getValue().trim());
+ }
+ }
+ }
+ }
+
+ private String buildString(boolean u, boolean p, String... parameters) {
+ StringBuilder buf = new StringBuilder();
+ if (protocol != null && protocol.length() > 0) {
+ buf.append(protocol);
+ buf.append("://");
+ }
+ if (u && username != null && username.length() > 0) {
+ buf.append(username);
+ if (password != null && password.length() > 0) {
+ buf.append(":");
+ buf.append(password);
+ }
+ buf.append("@");
+ }
+ if(host != null && host.length() > 0) {
+ buf.append(host);
+ if (port > 0) {
+ buf.append(":");
+ buf.append(port);
+ }
+ }
+ if (path != null && path.length() > 0) {
+ buf.append("/");
+ buf.append(path);
+ }
+ if (p) {
+ buildParameters(buf, true, parameters);
+ }
+ return buf.toString();
+ }
+
+ public java.net.URL toJavaURL() {
+ try {
+ return new java.net.URL(toString());
+ } catch (MalformedURLException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ public InetSocketAddress toInetSocketAddress() {
+ return new InetSocketAddress(host, port);
+ }
+
+ public String getServiceKey() {
+ String inf = getServiceInterface();
+ if (inf == null) return null;
+ StringBuilder buf = new StringBuilder();
+ String group = getParameter(Constants.GROUP_KEY);
+ if (group != null && group.length() > 0) {
+ buf.append(group).append("/");
+ }
+ buf.append(inf);
+ String version = getParameter(Constants.VERSION_KEY);
+ if (version != null && version.length() > 0) {
+ buf.append(":").append(version);
+ }
+ return buf.toString();
+ }
+
+ @Deprecated
+ public String getServiceName() {
+ return getServiceInterface();
+ }
+
+ public String getServiceInterface() {
+ return getParameter(Constants.INTERFACE_KEY, path);
+ }
+
+ public URL setServiceInterface(String service) {
+ return addParameter(Constants.INTERFACE_KEY, service);
+ }
+
+ /**
+ * @deprecated Replace to <code>getParameter(String, int)</code>
+ * @see #getParameter(String, int)
+ */
+ @Deprecated
+ public int getIntParameter(String key) {
+ return getParameter(key, 0);
+ }
+
+ /**
+ * @deprecated Replace to <code>getParameter(String, int)</code>
+ * @see #getParameter(String, int)
+ */
+ @Deprecated
+ public int getIntParameter(String key, int defaultValue) {
+ return getParameter(key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getPositiveParameter(String, int)</code>
+ * @see #getPositiveParameter(String, int)
+ */
+ @Deprecated
+ public int getPositiveIntParameter(String key, int defaultValue) {
+ return getPositiveParameter(key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getParameter(String, boolean)</code>
+ * @see #getParameter(String, boolean)
+ */
+ @Deprecated
+ public boolean getBooleanParameter(String key) {
+ return getParameter(key, false);
+ }
+
+ /**
+ * @deprecated Replace to <code>getParameter(String, boolean)</code>
+ * @see #getParameter(String, boolean)
+ */
+ @Deprecated
+ public boolean getBooleanParameter(String key, boolean defaultValue) {
+ return getParameter(key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodParameter(String, String, int)</code>
+ * @see #getMethodParameter(String, String, int)
+ */
+ @Deprecated
+ public int getMethodIntParameter(String method, String key) {
+ return getMethodParameter(method, key, 0);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodParameter(String, String, int)</code>
+ * @see #getMethodParameter(String, String, int)
+ */
+ @Deprecated
+ public int getMethodIntParameter(String method, String key, int defaultValue) {
+ return getMethodParameter(method, key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodPositiveParameter(String, String, int)</code>
+ * @see #getMethodPositiveParameter(String, String, int)
+ */
+ @Deprecated
+ public int getMethodPositiveIntParameter(String method, String key, int defaultValue) {
+ return getMethodPositiveParameter(method, key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodParameter(String, String, boolean)</code>
+ * @see #getMethodParameter(String, String, boolean)
+ */
+ @Deprecated
+ public boolean getMethodBooleanParameter(String method, String key) {
+ return getMethodParameter(method, key, false);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodParameter(String, String, boolean)</code>
+ * @see #getMethodParameter(String, String, boolean)
+ */
+ @Deprecated
+ public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) {
+ return getMethodParameter(method, key, defaultValue);
+ }
+
+ public static String encode(String value) {
+ if (value == null || value.length() == 0) {
+ return "";
+ }
+ try {
+ return URLEncoder.encode(value, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ public static String decode(String value) {
+ if (value == null || value.length() == 0) {
+ return "";
+ }
+ try {
+ return URLDecoder.decode(value, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((host == null) ? 0 : host.hashCode());
+ result = prime * result + ((parameters == null) ? 0 : parameters.hashCode());
+ result = prime * result + ((password == null) ? 0 : password.hashCode());
+ result = prime * result + ((path == null) ? 0 : path.hashCode());
+ result = prime * result + port;
+ result = prime * result + ((protocol == null) ? 0 : protocol.hashCode());
+ result = prime * result + ((username == null) ? 0 : username.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;
+ URL other = (URL) obj;
+ if (host == null) {
+ if (other.host != null)
+ return false;
+ } else if (!host.equals(other.host))
+ return false;
+ if (parameters == null) {
+ if (other.parameters != null)
+ return false;
+ } else if (!parameters.equals(other.parameters))
+ return false;
+ if (password == null) {
+ if (other.password != null)
+ return false;
+ } else if (!password.equals(other.password))
+ 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;
+ if (username == null) {
+ if (other.username != null)
+ return false;
+ } else if (!username.equals(other.username))
+ return false;
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Version.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Version.java
new file mode 100644
index 0000000..9e8e5fb
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Version.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * Version
+ *
+ * @author william.liangf
+ */
+public final class Version {
+
+ private Version() {}
+
+ private static final Logger logger = LoggerFactory.getLogger(Version.class);
+
+ private static final String VERSION = getVersion(Version.class, "2.0.0");
+
+ private static final boolean INTERNAL = hasResource("com/alibaba/dubbo/registry/support/remote/RemoteRegistry.class");
+
+ private static final boolean COMPATIBLE = hasResource("com/alibaba/dubbo/rpc/dubbo/internal/DubboRequest.class");
+
+ static {
+ // 检查是否存在重复的jar包
+ Version.checkDuplicate(Version.class);
+ }
+
+ public static String getVersion(){
+ return VERSION;
+ }
+
+ public static boolean isInternalVersion() {
+ return INTERNAL;
+ }
+
+ public static boolean isCompatibleVersion() {
+ return COMPATIBLE;
+ }
+
+ private static boolean hasResource(String path) {
+ try {
+ return Version.class.getClassLoader().getResource(path) != null;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ public static String getVersion(Class<?> cls, String defaultVersion) {
+ try {
+ // 首先查找MANIFEST.MF规范中的版本号
+ String version = cls.getPackage().getImplementationVersion();
+ if (version == null || version.length() == 0) {
+ version = cls.getPackage().getSpecificationVersion();
+ }
+ if (version == null || version.length() == 0) {
+ // 如果规范中没有版本号,基于jar包名获取版本号
+ String file = cls.getProtectionDomain().getCodeSource().getLocation().getFile();
+ if (file != null && file.length() > 0 && file.endsWith(".jar")) {
+ file = file.substring(0, file.length() - 4);
+ int i = file.lastIndexOf('/');
+ if (i >= 0) {
+ file = file.substring(i + 1);
+ }
+ i = file.indexOf("-");
+ if (i >= 0) {
+ file = file.substring(i + 1);
+ }
+ while (file.length() > 0 && ! Character.isDigit(file.charAt(0))) {
+ i = file.indexOf("-");
+ if (i >= 0) {
+ file = file.substring(i + 1);
+ } else {
+ break;
+ }
+ }
+ version = file;
+ }
+ }
+ // 返回版本号,如果为空返回缺省版本号
+ return version == null || version.length() == 0 ? defaultVersion : version;
+ } catch (Throwable e) { // 防御性容错
+ // 忽略异常,返回缺省版本号
+ logger.error(e.getMessage(), e);
+ return defaultVersion;
+ }
+ }
+
+ public static void checkDuplicate(Class<?> cls) {
+ checkDuplicate(cls.getName().replace('.', '/') + ".class");
+ }
+
+ public static void checkDuplicate(String path) {
+ try {
+ // 在ClassPath搜文件
+ Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(path);
+ Set<String> files = new HashSet<String>();
+ while (urls.hasMoreElements()) {
+ URL url = urls.nextElement();
+ if (url != null) {
+ String file = url.getFile();
+ if (file != null && file.length() > 0) {
+ files.add(file);
+ }
+ }
+ }
+ // 如果有多个,就表示重复
+ if (files.size() > 1) {
+ logger.error("Duplicate class " + path + " in " + files.size() + " jar " + files);
+ }
+ } catch (Throwable e) { // 防御性容错
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.java
new file mode 100644
index 0000000..efe08a0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.LoaderClassPath;
+import javassist.NotFoundException;
+
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+
+/**
+ * ClassGenerator
+ *
+ * @author qian.lei
+ */
+
+public final class ClassGenerator
+{
+ public static interface DC{} // dynamic class tag interface.
+
+ private static final AtomicLong CLASS_NAME_COUNTER = new AtomicLong(0);
+
+ private static final String SIMPLE_NAME_TAG = "<init>";
+
+ private static final Map<ClassLoader, ClassPool> POOL_MAP = new ConcurrentHashMap<ClassLoader, ClassPool>(); //ClassLoader - ClassPool
+
+ public static ClassGenerator newInstance()
+ {
+ return new ClassGenerator(getClassPool(Thread.currentThread().getContextClassLoader()));
+ }
+
+ public static ClassGenerator newInstance(ClassLoader loader)
+ {
+ return new ClassGenerator(getClassPool(loader));
+ }
+
+ public static boolean isDynamicClass(Class<?> cl)
+ {
+ return ClassGenerator.DC.class.isAssignableFrom(cl);
+ }
+
+ public static ClassPool getClassPool(ClassLoader loader)
+ {
+ if( loader == null )
+ return ClassPool.getDefault();
+
+ ClassPool pool = POOL_MAP.get(loader);
+ if( pool == null )
+ {
+ pool = new ClassPool(true);
+ pool.appendClassPath(new LoaderClassPath(loader));
+ POOL_MAP.put(loader, pool);
+ }
+ return pool;
+ }
+
+ private ClassPool mPool;
+
+ private CtClass mCtc;
+
+ private String mClassName, mSuperClass;
+
+ private Set<String> mInterfaces;
+
+ private List<String> mFields, mConstructors, mMethods;
+
+ private Map<String, Method> mCopyMethods; // <method desc,method instance>
+
+ private Map<String, Constructor<?>> mCopyConstructors; // <constructor desc,constructor instance>
+
+ private boolean mDefaultConstructor = false;
+
+ private ClassGenerator(){}
+
+ private ClassGenerator(ClassPool pool)
+ {
+ mPool = pool;
+ }
+
+ public String getClassName()
+ {
+ return mClassName;
+ }
+
+ public ClassGenerator setClassName(String name)
+ {
+ mClassName = name;
+ return this;
+ }
+
+ public ClassGenerator addInterface(String cn)
+ {
+ if( mInterfaces == null )
+ mInterfaces = new HashSet<String>();
+ mInterfaces.add(cn);
+ return this;
+ }
+
+ public ClassGenerator addInterface(Class<?> cl)
+ {
+ return addInterface(cl.getName());
+ }
+
+ public ClassGenerator setSuperClass(String cn)
+ {
+ mSuperClass = cn;
+ return this;
+ }
+
+ public ClassGenerator setSuperClass(Class<?> cl)
+ {
+ mSuperClass = cl.getName();
+ return this;
+ }
+
+ public ClassGenerator addField(String code)
+ {
+ if( mFields == null )
+ mFields = new ArrayList<String>();
+ mFields.add(code);
+ return this;
+ }
+
+ public ClassGenerator addField(String name, int mod, Class<?> type)
+ {
+ return addField(name, mod, type, null);
+ }
+
+ public ClassGenerator addField(String name, int mod, Class<?> type, String def)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(type)).append(' ');
+ sb.append(name);
+ if( def != null && def.length() > 0 )
+ {
+ sb.append('=');
+ sb.append(def);
+ }
+ sb.append(';');
+ return addField(sb.toString());
+ }
+
+ public ClassGenerator addMethod(String code)
+ {
+ if( mMethods == null )
+ mMethods = new ArrayList<String>();
+ mMethods.add(code);
+ return this;
+ }
+
+ public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, String body)
+ {
+ return addMethod(name, mod, rt, pts, null, body);
+ }
+
+ public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, Class<?>[] ets, String body)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(rt)).append(' ').append(name);
+ sb.append('(');
+ for(int i=0;i<pts.length;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(ReflectUtils.getName(pts[i]));
+ sb.append(" arg").append(i);
+ }
+ sb.append(')');
+ if( ets != null && ets.length > 0 )
+ {
+ sb.append(" throws ");
+ for(int i=0;i<ets.length;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(ReflectUtils.getName(ets[i]));
+ }
+ }
+ sb.append('{').append(body).append('}');
+ return addMethod(sb.toString());
+ }
+
+ public ClassGenerator addMethod(Method m)
+ {
+ addMethod(m.getName(), m);
+ return this;
+ }
+
+ public ClassGenerator addMethod(String name, Method m)
+ {
+ String desc = name + ReflectUtils.getDescWithoutMethodName(m);
+ addMethod(':' + desc);
+ if( mCopyMethods == null )
+ mCopyMethods = new ConcurrentHashMap<String, Method>(8);
+ mCopyMethods.put(desc, m);
+ return this;
+ }
+
+ public ClassGenerator addConstructor(String code)
+ {
+ if( mConstructors == null )
+ mConstructors = new LinkedList<String>();
+ mConstructors.add(code);
+ return this;
+ }
+
+ public ClassGenerator addConstructor(int mod, Class<?>[] pts, String body)
+ {
+ return addConstructor(mod, pts, null, body);
+ }
+
+ public ClassGenerator addConstructor(int mod, Class<?>[] pts, Class<?>[] ets, String body)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(modifier(mod)).append(' ').append(SIMPLE_NAME_TAG);
+ sb.append('(');
+ for(int i=0;i<pts.length;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(ReflectUtils.getName(pts[i]));
+ sb.append(" arg").append(i);
+ }
+ sb.append(')');
+ if( ets != null && ets.length > 0 )
+ {
+ sb.append(" throws ");
+ for(int i=0;i<ets.length;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(ReflectUtils.getName(ets[i]));
+ }
+ }
+ sb.append('{').append(body).append('}');
+ return addConstructor(sb.toString());
+ }
+
+ public ClassGenerator addConstructor(Constructor<?> c)
+ {
+ String desc = ReflectUtils.getDesc(c);
+ addConstructor(":"+desc);
+ if( mCopyConstructors == null )
+ mCopyConstructors = new ConcurrentHashMap<String, Constructor<?>>(4);
+ mCopyConstructors.put(desc, c);
+ return this;
+ }
+
+ public ClassGenerator addDefaultConstructor()
+ {
+ mDefaultConstructor = true;
+ return this;
+ }
+
+ public ClassPool getClassPool() {
+ return mPool;
+ }
+
+ public Class<?> toClass()
+ {
+ if( mCtc != null )
+ mCtc.detach();
+ long id = CLASS_NAME_COUNTER.getAndIncrement();
+ try
+ {
+ CtClass ctcs = mSuperClass == null ? null : mPool.get(mSuperClass);
+ if( mClassName == null )
+ mClassName = ( mSuperClass == null || javassist.Modifier.isPublic(ctcs.getModifiers())
+ ? ClassGenerator.class.getName() : mSuperClass + "$sc" ) + id;
+ mCtc = mPool.makeClass(mClassName);
+ if( mSuperClass != null )
+ mCtc.setSuperclass(ctcs);
+ mCtc.addInterface(mPool.get(DC.class.getName())); // add dynamic class tag.
+ if( mInterfaces != null )
+ for( String cl : mInterfaces ) mCtc.addInterface(mPool.get(cl));
+ if( mFields != null )
+ for( String code : mFields ) mCtc.addField(CtField.make(code, mCtc));
+ if( mMethods != null )
+ {
+ for( String code : mMethods )
+ {
+ if( code.charAt(0) == ':' )
+ mCtc.addMethod(CtNewMethod.copy(getCtMethod(mCopyMethods.get(code.substring(1))), code.substring(1, code.indexOf('(')), mCtc, null));
+ else
+ mCtc.addMethod(CtNewMethod.make(code, mCtc));
+ }
+ }
+ if( mDefaultConstructor )
+ mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
+ if( mConstructors != null )
+ {
+ for( String code : mConstructors )
+ {
+ if( code.charAt(0) == ':' )
+ {
+ mCtc.addConstructor(CtNewConstructor.copy(getCtConstructor(mCopyConstructors.get(code.substring(1))), mCtc, null));
+ }
+ else
+ {
+ String[] sn = mCtc.getSimpleName().split("\\$+"); // inner class name include $.
+ mCtc.addConstructor(CtNewConstructor.make(code.replaceFirst(SIMPLE_NAME_TAG, sn[sn.length-1]), mCtc));
+ }
+ }
+ }
+ return mCtc.toClass();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(NotFoundException e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ catch(CannotCompileException e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ public void release()
+ {
+ if( mCtc != null ) mCtc.detach();
+ if( mInterfaces != null ) mInterfaces.clear();
+ if( mFields != null ) mFields.clear();
+ if( mMethods != null ) mMethods.clear();
+ if( mConstructors != null ) mConstructors.clear();
+ if( mCopyMethods != null ) mCopyMethods.clear();
+ if( mCopyConstructors != null ) mCopyConstructors.clear();
+ }
+
+ private CtClass getCtClass(Class<?> c) throws NotFoundException
+ {
+ return mPool.get(c.getName());
+ }
+
+ private CtMethod getCtMethod(Method m) throws NotFoundException
+ {
+ return getCtClass(m.getDeclaringClass()).getMethod(m.getName(),ReflectUtils.getDescWithoutMethodName(m));
+ }
+
+ private CtConstructor getCtConstructor(Constructor<?> c) throws NotFoundException
+ {
+ return getCtClass(c.getDeclaringClass()).getConstructor(ReflectUtils.getDesc(c));
+ }
+
+ private static String modifier(int mod)
+ {
+ if( Modifier.isPublic(mod) ) return "public";
+ if( Modifier.isProtected(mod) ) return "protected";
+ if( Modifier.isPrivate(mod) ) return "private";
+ return "";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Mixin.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Mixin.java
new file mode 100644
index 0000000..751a662
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Mixin.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.dubbo.common.utils.ClassHelper;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+
+/**
+ * Mixin
+ *
+ * @author qian.lei
+ */
+
+public abstract class Mixin
+{
+ private static AtomicLong MIXIN_CLASS_COUNTER = new AtomicLong(0);
+
+ private static final String PACKAGE_NAME = Mixin.class.getPackage().getName();
+
+ public static interface MixinAware{ void setMixinInstance(Object instance); }
+
+ /**
+ * mixin interface and delegates.
+ * all class must be public.
+ *
+ * @param ics interface class array.
+ * @param dc delegate class.
+ * @return Mixin instance.
+ */
+ public static Mixin mixin(Class<?>[] ics, Class<?> dc)
+ {
+ return mixin(ics, new Class[]{dc});
+ }
+
+ /**
+ * mixin interface and delegates.
+ * all class must be public.
+ *
+ * @param ics interface class array.
+ * @param dc delegate class.
+ * @param cl class loader.
+ * @return Mixin instance.
+ */
+ public static Mixin mixin(Class<?>[] ics, Class<?> dc, ClassLoader cl)
+ {
+ return mixin(ics, new Class[]{dc}, cl);
+ }
+
+ /**
+ * mixin interface and delegates.
+ * all class must be public.
+ *
+ * @param ics interface class array.
+ * @param dcs delegate class array.
+ * @return Mixin instance.
+ */
+ public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs)
+ {
+ return mixin(ics, dcs, ClassHelper.getClassLoader(ics[0]));
+ }
+
+ /**
+ * mixin interface and delegates.
+ * all class must be public.
+ *
+ * @param ics interface class array.
+ * @param dcs delegate class array.
+ * @param cl class loader.
+ * @return Mixin instance.
+ */
+ public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs, ClassLoader cl)
+ {
+ assertInterfaceArray(ics);
+
+ long id = MIXIN_CLASS_COUNTER.getAndIncrement();
+ String pkg = null;
+ ClassGenerator ccp = null, ccm = null;
+ try
+ {
+ ccp = ClassGenerator.newInstance(cl);
+
+ // impl constructor
+ StringBuilder code = new StringBuilder();
+ for(int i=0;i<dcs.length;i++)
+ {
+ if( !Modifier.isPublic(dcs[i].getModifiers()) )
+ {
+ String npkg = dcs[i].getPackage().getName();
+ if( pkg == null )
+ {
+ pkg = npkg;
+ }
+ else
+ {
+ if( !pkg.equals(npkg) )
+ throw new IllegalArgumentException("non-public interfaces class from different packages");
+ }
+ }
+
+ ccp.addField("private " + dcs[i].getName() + " d" + i + ";");
+
+ code.append("d").append(i).append(" = (").append(dcs[i].getName()).append(")$1[").append(i).append("];\n");
+ if( MixinAware.class.isAssignableFrom(dcs[i]) )
+ code.append("d").append(i).append(".setMixinInstance(this);\n");
+ }
+ ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{ Object[].class }, code.toString());
+
+ // impl methods.
+ Set<String> worked = new HashSet<String>();
+ for(int i=0;i<ics.length;i++)
+ {
+ if( !Modifier.isPublic(ics[i].getModifiers()) )
+ {
+ String npkg = ics[i].getPackage().getName();
+ if( pkg == null )
+ {
+ pkg = npkg;
+ }
+ else
+ {
+ if( !pkg.equals(npkg) )
+ throw new IllegalArgumentException("non-public delegate class from different packages");
+ }
+ }
+
+ ccp.addInterface(ics[i]);
+
+ for( Method method : ics[i].getMethods() )
+ {
+ if( "java.lang.Object".equals(method.getDeclaringClass().getName()) )
+ continue;
+
+ String desc = ReflectUtils.getDesc(method);
+ if( worked.contains(desc) )
+ continue;
+ worked.add(desc);
+
+ int ix = findMethod(dcs, desc);
+ if( ix < 0 )
+ throw new RuntimeException("Missing method [" + desc + "] implement.");
+
+ Class<?> rt = method.getReturnType();
+ String mn = method.getName();
+ if( Void.TYPE.equals(rt) )
+ ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
+ "d" + ix + "." + mn + "($$);");
+ else
+ ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
+ "return ($r)d" + ix + "." + mn + "($$);");
+ }
+ }
+
+ if( pkg == null )
+ pkg = PACKAGE_NAME;
+
+ // create MixinInstance class.
+ String micn = pkg + ".mixin" + id;
+ ccp.setClassName(micn);
+ ccp.toClass();
+
+ // create Mixin class.
+ String fcn = Mixin.class.getName() + id;
+ ccm = ClassGenerator.newInstance(cl);
+ ccm.setClassName(fcn);
+ ccm.addDefaultConstructor();
+ ccm.setSuperClass(Mixin.class.getName());
+ ccm.addMethod("public Object newInstance(Object[] delegates){ return new " + micn + "($1); }");
+ Class<?> mixin = ccm.toClass();
+ return (Mixin)mixin.newInstance();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ finally
+ {
+ // release ClassGenerator
+ if( ccp != null )
+ ccp.release();
+ if( ccm != null )
+ ccm.release();
+ }
+ }
+
+ /**
+ * new Mixin instance.
+ *
+ * @param ds delegates instance.
+ * @return instance.
+ */
+ abstract public Object newInstance(Object[] ds);
+
+ protected Mixin(){}
+
+ private static int findMethod(Class<?>[] dcs, String desc)
+ {
+ Class<?> cl;
+ Method[] methods;
+ for(int i=0;i<dcs.length;i++)
+ {
+ cl = dcs[i];
+ methods = cl.getMethods();
+ for( Method method : methods )
+ {
+ if( desc.equals(ReflectUtils.getDesc(method)) )
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static void assertInterfaceArray(Class<?>[] ics)
+ {
+ for(int i=0;i<ics.length;i++)
+ if( !ics[i].isInterface() )
+ throw new RuntimeException("Class " + ics[i].getName() + " is not a interface.");
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchMethodException.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchMethodException.java
new file mode 100644
index 0000000..a8083e8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchMethodException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+/**
+ * NoSuchMethodException.
+ *
+ * @author qian.lei
+ */
+
+public class NoSuchMethodException extends RuntimeException
+{
+ private static final long serialVersionUID = -2725364246023268766L;
+
+ public NoSuchMethodException()
+ {
+ super();
+ }
+
+ public NoSuchMethodException(String msg)
+ {
+ super(msg);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchPropertyException.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchPropertyException.java
new file mode 100644
index 0000000..1384fdd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchPropertyException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+/**
+ * NoSuchPropertyException.
+ *
+ * @author qian.lei
+ */
+
+public class NoSuchPropertyException extends RuntimeException
+{
+ private static final long serialVersionUID = -2725364246023268766L;
+
+ public NoSuchPropertyException()
+ {
+ super();
+ }
+
+ public NoSuchPropertyException(String msg)
+ {
+ super(msg);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Proxy.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Proxy.java
new file mode 100644
index 0000000..03b27f9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Proxy.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.dubbo.common.utils.ClassHelper;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+
+/**
+ * Proxy.
+ *
+ * @author qian.lei
+ */
+
+public abstract class Proxy
+{
+ private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0);
+
+ private static final String PACKAGE_NAME = Proxy.class.getPackage().getName();
+
+ public static final InvocationHandler RETURN_NULL_INVOKER = new InvocationHandler(){
+ public Object invoke(Object proxy, Method method, Object[] args){ return null; }
+ };
+
+ public static final InvocationHandler THROW_UNSUPPORTED_INVOKER = new InvocationHandler(){
+ public Object invoke(Object proxy, Method method, Object[] args){ throw new UnsupportedOperationException("Method [" + ReflectUtils.getName(method) + "] unimplemented."); }
+ };
+
+ private static final Map<ClassLoader, Map<String, Object>> ProxyCacheMap = new WeakHashMap<ClassLoader, Map<String, Object>>();
+
+ private static final Object PendingGenerationMarker = new Object();
+
+ /**
+ * Get proxy.
+ *
+ * @param ics interface class array.
+ * @return Proxy instance.
+ */
+ public static Proxy getProxy(Class<?>... ics)
+ {
+ return getProxy(ClassHelper.getClassLoader(ics[0]), ics);
+ }
+
+ /**
+ * Get proxy.
+ * @param cl class loader.
+ * @param ics interface class array.
+ *
+ * @return Proxy instance.
+ */
+ public static Proxy getProxy(ClassLoader cl, Class<?>... ics)
+ {
+ if( ics.length > 65535 )
+ throw new IllegalArgumentException("interface limit exceeded");
+
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<ics.length;i++)
+ {
+ String itf = ics[i].getName();
+ if( !ics[i].isInterface() )
+ throw new RuntimeException(itf + " is not a interface.");
+
+ Class<?> tmp = null;
+ try
+ {
+ tmp = Class.forName(itf, false, cl);
+ }
+ catch(ClassNotFoundException e)
+ {}
+
+ if( tmp != ics[i] )
+ throw new IllegalArgumentException(ics[i] + " is not visible from class loader");
+
+ sb.append(itf).append(';');
+ }
+
+ // use interface class name list as key.
+ String key = sb.toString();
+
+ // get cache by class loader.
+ Map<String, Object> cache;
+ synchronized( ProxyCacheMap )
+ {
+ cache = ProxyCacheMap.get(cl);
+ if( cache == null )
+ {
+ cache = new HashMap<String, Object>();
+ ProxyCacheMap.put(cl, cache);
+ }
+ }
+
+ Proxy proxy = null;
+ synchronized( cache )
+ {
+ do
+ {
+ Object value = cache.get(key);
+ if( value instanceof Reference<?> )
+ {
+ proxy = (Proxy)((Reference<?>)value).get();
+ if( proxy != null )
+ return proxy;
+ }
+
+ if( value == PendingGenerationMarker )
+ {
+ try{ cache.wait(); }catch(InterruptedException e){}
+ }
+ else
+ {
+ cache.put(key, PendingGenerationMarker);
+ break;
+ }
+ }
+ while( true );
+ }
+
+ long id = PROXY_CLASS_COUNTER.getAndIncrement();
+ String pkg = null;
+ ClassGenerator ccp = null, ccm = null;
+ try
+ {
+ ccp = ClassGenerator.newInstance(cl);
+
+ Set<String> worked = new HashSet<String>();
+ List<Method> methods = new ArrayList<Method>();
+
+ for(int i=0;i<ics.length;i++)
+ {
+ if( !Modifier.isPublic(ics[i].getModifiers()) )
+ {
+ String npkg = ics[i].getPackage().getName();
+ if( pkg == null )
+ {
+ pkg = npkg;
+ }
+ else
+ {
+ if( !pkg.equals(npkg) )
+ throw new IllegalArgumentException("non-public interfaces from different packages");
+ }
+ }
+ ccp.addInterface(ics[i]);
+
+ for( Method method : ics[i].getMethods() )
+ {
+ String desc = ReflectUtils.getDesc(method);
+ if( worked.contains(desc) )
+ continue;
+ worked.add(desc);
+
+ int ix = methods.size();
+ Class<?> rt = method.getReturnType();
+ Class<?>[] pts = method.getParameterTypes();
+
+ StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
+ for(int j=0;j<pts.length;j++)
+ code.append(" args[").append(j).append("] = ($w)$").append(j+1).append(";");
+ code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");
+ if( !Void.TYPE.equals(rt) )
+ code.append(" return ").append(asArgument(rt, "ret")).append(";");
+
+ methods.add(method);
+ ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
+ }
+ }
+
+ if( pkg == null )
+ pkg = PACKAGE_NAME;
+
+ // create ProxyInstance class.
+ String pcn = pkg + ".proxy" + id;
+ ccp.setClassName(pcn);
+ ccp.addField("public static java.lang.reflect.Method[] methods;");
+ ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
+ ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{ InvocationHandler.class }, new Class<?>[0], "handler=$1;");
+ Class<?> clazz = ccp.toClass();
+ clazz.getField("methods").set(null, methods.toArray(new Method[0]));
+
+ // create Proxy class.
+ String fcn = Proxy.class.getName() + id;
+ ccm = ClassGenerator.newInstance(cl);
+ ccm.setClassName(fcn);
+ ccm.addDefaultConstructor();
+ ccm.setSuperClass(Proxy.class);
+ ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
+ Class<?> pc = ccm.toClass();
+ proxy = (Proxy)pc.newInstance();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ finally
+ {
+ // release ClassGenerator
+ if( ccp != null )
+ ccp.release();
+ if( ccm != null )
+ ccm.release();
+ synchronized( cache )
+ {
+ if( proxy == null )
+ cache.remove(key);
+ else
+ cache.put(key, new WeakReference<Proxy>(proxy));
+ cache.notifyAll();
+ }
+ }
+ return proxy;
+ }
+
+ /**
+ * get instance with default handler.
+ *
+ * @return instance.
+ */
+ public Object newInstance()
+ {
+ return newInstance(THROW_UNSUPPORTED_INVOKER);
+ }
+
+ /**
+ * get instance with special handler.
+ *
+ * @return instance.
+ */
+ abstract public Object newInstance(InvocationHandler handler);
+
+ protected Proxy(){}
+
+ private static String asArgument(Class<?> cl, String name)
+ {
+ if( cl.isPrimitive() )
+ {
+ if( Boolean.TYPE == cl )
+ return name + "==null?false:((Boolean)" + name + ").booleanValue()";
+ if( Byte.TYPE == cl )
+ return name + "==null?(byte)0:((Byte)" + name + ").byteValue()";
+ if( Character.TYPE == cl )
+ return name + "==null?(char)0:((Character)" + name + ").charValue()";
+ if( Double.TYPE == cl )
+ return name + "==null?(double)0:((Double)" + name + ").doubleValue()";
+ if( Float.TYPE == cl )
+ return name + "==null?(float)0:((Float)" + name + ").floatValue()";
+ if( Integer.TYPE == cl )
+ return name + "==null?(int)0:((Integer)" + name + ").intValue()";
+ if( Long.TYPE == cl )
+ return name + "==null?(long)0:((Long)" + name + ").longValue()";
+ if( Short.TYPE == cl )
+ return name + "==null?(short)0:((Short)" + name + ").shortValue()";
+ throw new RuntimeException(name+" is unknown primitive type.");
+ }
+ return "(" + ReflectUtils.getName(cl) + ")"+name;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Wrapper.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Wrapper.java
new file mode 100644
index 0000000..3a7b488
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Wrapper.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Matcher;
+
+import com.alibaba.dubbo.common.utils.ClassHelper;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+
+/**
+ * Wrapper.
+ *
+ * @author qian.lei
+ */
+
+public abstract class Wrapper
+{
+ private static AtomicLong WRAPPER_CLASS_COUNTER = new AtomicLong(0);
+
+ private static final Map<Class<?>, Wrapper> WRAPPER_MAP = new ConcurrentHashMap<Class<?>, Wrapper>(); //class wrapper map
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private static final String[] OBJECT_METHODS = new String[]{"getClass", "hashCode", "toString", "equals"};
+
+ private static final Wrapper OBJECT_WRAPPER = new Wrapper(){
+ public String[] getMethodNames(){ return OBJECT_METHODS; }
+ public String[] getDeclaredMethodNames(){ return OBJECT_METHODS; }
+ public String[] getPropertyNames(){ return EMPTY_STRING_ARRAY; }
+ public Class<?> getPropertyType(String pn){ return null; }
+ public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException{ throw new NoSuchPropertyException("Property [" + pn + "] not found."); }
+ public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException{ throw new NoSuchPropertyException("Property [" + pn + "] not found."); }
+ public boolean hasProperty(String name){ return false; }
+ public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException
+ {
+ if( "getClass".equals(mn) ) return instance.getClass();
+ if( "hashCode".equals(mn) ) return instance.hashCode();
+ if( "toString".equals(mn) ) return instance.toString();
+ if( "equals".equals(mn) )
+ {
+ if( args.length == 1 ) return instance.equals(args[0]);
+ throw new IllegalArgumentException("Invoke method [" + mn + "] argument number error.");
+ }
+ throw new NoSuchMethodException("Method [" + mn + "] not found.");
+ }
+ };
+
+ /**
+ * get wrapper.
+ *
+ * @param c Class instance.
+ * @return Wrapper instance(not null).
+ */
+ public static Wrapper getWrapper(Class<?> c)
+ {
+ while( ClassGenerator.isDynamicClass(c) ) // can not wrapper on dynamic class.
+ c = c.getSuperclass();
+
+ if( c == Object.class )
+ return OBJECT_WRAPPER;
+
+ Wrapper ret = WRAPPER_MAP.get(c);
+ if( ret == null )
+ {
+ ret = makeWrapper(c);
+ WRAPPER_MAP.put(c,ret);
+ }
+ return ret;
+ }
+ /**
+ * get property name array.
+ *
+ * @return property name array.
+ */
+ abstract public String[] getPropertyNames();
+
+ /**
+ * get property type.
+ *
+ * @param pn property name.
+ * @return Property type or nul.
+ */
+ abstract public Class<?> getPropertyType(String pn);
+
+ /**
+ * has property.
+ *
+ * @param name property name.
+ * @return has or has not.
+ */
+ abstract public boolean hasProperty(String name);
+
+ /**
+ * get property value.
+ *
+ * @param instance instance.
+ * @param pn property name.
+ * @return value.
+ */
+ abstract public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException, IllegalArgumentException;
+
+ /**
+ * set property value.
+ *
+ * @param instance instance.
+ * @param pn property name.
+ * @param pv property value.
+ */
+ abstract public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException, IllegalArgumentException;
+
+ /**
+ * get property value.
+ *
+ * @param instance instance.
+ * @param pns property name array.
+ * @return value array.
+ */
+ public Object[] getPropertyValues(Object instance, String[] pns) throws NoSuchPropertyException, IllegalArgumentException
+ {
+ Object[] ret = new Object[pns.length];
+ for(int i=0;i<ret.length;i++)
+ ret[i] = getPropertyValue(instance, pns[i]);
+ return ret;
+ }
+
+ /**
+ * set property value.
+ *
+ * @param instance instance.
+ * @param pns property name array.
+ * @param pvs property value array.
+ */
+ public void setPropertyValues(Object instance, String[] pns, Object[] pvs) throws NoSuchPropertyException, IllegalArgumentException
+ {
+ if( pns.length != pvs.length )
+ throw new IllegalArgumentException("pns.length != pvs.length");
+
+ for(int i=0;i<pns.length;i++)
+ setPropertyValue(instance, pns[i], pvs[i]);
+ }
+
+ /**
+ * get method name array.
+ *
+ * @return method name array.
+ */
+ abstract public String[] getMethodNames();
+
+ /**
+ * get method name array.
+ *
+ * @return method name array.
+ */
+ abstract public String[] getDeclaredMethodNames();
+
+ /**
+ * has method.
+ *
+ * @param name method name.
+ * @return has or has not.
+ */
+ public boolean hasMethod(String name)
+ {
+ for( String mn : getMethodNames() )
+ if( mn.equals(name) ) return true;
+ return false;
+ }
+
+ /**
+ * invoke method.
+ *
+ * @param instance instance.
+ * @param mn method name.
+ * @param types
+ * @param args argument array.
+ * @return return value.
+ */
+ abstract public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException, InvocationTargetException;
+
+ private static Wrapper makeWrapper(Class<?> c)
+ {
+ if( c.isPrimitive() )
+ throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);
+
+ String name = c.getName();
+ ClassLoader cl = ClassHelper.getClassLoader(c);
+
+ StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
+ StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
+ StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");
+
+ c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
+ c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
+ c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); } try{");
+
+ Map<String, Class<?>> pts = new HashMap<String, Class<?>>(); // <property name, property types>
+ Map<String, Method> ms = new LinkedHashMap<String, Method>(); // <method desc, Method instance>
+ List<String> mns = new ArrayList<String>(); // method names.
+ List<String> dmns = new ArrayList<String>(); // declaring method names.
+
+ // get all public field.
+ for( Field f : c.getFields() )
+ {
+ String fn = f.getName();
+ Class<?> ft = f.getType();
+ if( Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers()) )
+ continue;
+
+ c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");
+ c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");
+ pts.put(fn, ft);
+ }
+
+ Method[] methods = c.getMethods();
+ // get all public method.
+ for( Method m : methods )
+ {
+ if( m.getDeclaringClass() == Object.class ) //ignore Object's method.
+ continue;
+
+ String mn = m.getName();
+ c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");
+ int len = m.getParameterTypes().length;
+ c3.append(" && ").append(" $3.length == ").append(len);
+
+ boolean override = false;
+ for( Method m2 : methods ) {
+ if (m != m2 && m.getName().equals(m2.getName())) {
+ override = true;
+ break;
+ }
+ }
+ if (override) {
+ if (len > 0) {
+ for (int l = 0; l < len; l ++) {
+ c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
+ .append(m.getParameterTypes()[l].getName()).append("\")");
+ }
+ }
+ }
+
+ c3.append(" ) { ");
+
+ if( m.getReturnType() == Void.TYPE )
+ c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
+ else
+ c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");
+
+ c3.append(" }");
+
+ mns.add(mn);
+ if( m.getDeclaringClass() == c )
+ dmns.add(mn);
+ ms.put(ReflectUtils.getDesc(m), m);
+ }
+ c3.append(" } catch(Throwable e) { " );
+ c3.append(" throw new java.lang.reflect.InvocationTargetException(e); " );
+ c3.append(" }");
+ c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");
+
+ // deal with get/set method.
+ Matcher matcher;
+ for( Map.Entry<String,Method> entry : ms.entrySet() )
+ {
+ String md = entry.getKey();
+ Method method = (Method)entry.getValue();
+ if( ( matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )
+ {
+ String pn = propertyName(matcher.group(1));
+ c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
+ pts.put(pn, method.getReturnType());
+ }
+ else if( ( matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md) ).matches() )
+ {
+ String pn = propertyName(matcher.group(1));
+ c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
+ pts.put(pn, method.getReturnType());
+ }
+ else if( ( matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )
+ {
+ Class<?> pt = method.getParameterTypes()[0];
+ String pn = propertyName(matcher.group(1));
+ c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt,"$3")).append("); return; }");
+ pts.put(pn, pt);
+ }
+ }
+ c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");
+ c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");
+
+ // make class
+ long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
+ ClassGenerator cc = ClassGenerator.newInstance(cl);
+ cc.setClassName( ( Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw" ) + id );
+ cc.setSuperClass(Wrapper.class);
+
+ cc.addDefaultConstructor();
+ cc.addField("public static String[] pns;"); // property name array.
+ cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.
+ cc.addField("public static String[] mns;"); // all method name array.
+ cc.addField("public static String[] dmns;"); // declared method name array.
+ for(int i=0,len=ms.size();i<len;i++)
+ cc.addField("public static Class[] mts" + i + ";");
+
+ cc.addMethod("public String[] getPropertyNames(){ return pns; }");
+ cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
+ cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
+ cc.addMethod("public String[] getMethodNames(){ return mns; }");
+ cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
+ cc.addMethod(c1.toString());
+ cc.addMethod(c2.toString());
+ cc.addMethod(c3.toString());
+
+ try
+ {
+ Class<?> wc = cc.toClass();
+ // setup static field.
+ wc.getField("pts").set(null, pts);
+ wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
+ wc.getField("mns").set(null, mns.toArray(new String[0]));
+ wc.getField("dmns").set(null, dmns.toArray(new String[0]));
+ int ix = 0;
+ for( Method m : ms.values() )
+ wc.getField("mts" + ix++).set(null, m.getParameterTypes());
+ return (Wrapper)wc.newInstance();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(Throwable e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ finally
+ {
+ cc.release();
+ ms.clear();
+ mns.clear();
+ dmns.clear();
+ }
+ }
+
+ private static String arg(Class<?> cl, String name)
+ {
+ if( cl.isPrimitive() )
+ {
+ if( cl == Boolean.TYPE )
+ return "((Boolean)" + name + ").booleanValue()";
+ if( cl == Byte.TYPE )
+ return "((Byte)" + name + ").byteValue()";
+ if( cl == Character.TYPE )
+ return "((Character)" + name + ").charValue()";
+ if( cl == Double.TYPE )
+ return "((Number)" + name + ").doubleValue()";
+ if( cl == Float.TYPE )
+ return "((Number)" + name + ").floatValue()";
+ if( cl == Integer.TYPE )
+ return "((Number)" + name + ").intValue()";
+ if( cl == Long.TYPE )
+ return "((Number)" + name + ").longValue()";
+ if( cl == Short.TYPE )
+ return "((Number)" + name + ").shortValue()";
+ throw new RuntimeException("Unknown primitive type: " + cl.getName());
+ }
+ return "(" + ReflectUtils.getName(cl) + ")" + name;
+ }
+
+ private static String args(Class<?>[] cs,String name)
+ {
+ int len = cs.length;
+ if( len == 0 ) return "";
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<len;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(arg(cs[i],name+"["+i+"]"));
+ }
+ return sb.toString();
+ }
+
+ private static String propertyName(String pn)
+ {
+ return pn.length() == 1 || Character.isLowerCase(pn.charAt(1)) ? Character.toLowerCase(pn.charAt(0)) + pn.substring(1) : pn;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/Compiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/Compiler.java
new file mode 100644
index 0000000..a703196
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/Compiler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.compiler;
+
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * Compiler. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@SPI("javassist")
+public interface Compiler {
+
+ /**
+ * Compile java source code.
+ *
+ * @param code Java source code
+ * @param classLoader TODO
+ * @return Compiled class
+ */
+ Class<?> compile(String code, ClassLoader classLoader);
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AbstractCompiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AbstractCompiler.java
new file mode 100644
index 0000000..6f09281
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AbstractCompiler.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.compiler.support;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.compiler.Compiler;
+
+/**
+ * Abstract compiler. (SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractCompiler implements Compiler {
+
+ private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*);");
+
+ private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s+");
+
+ public Class<?> compile(String code, ClassLoader classLoader) {
+ code = code.trim();
+ Matcher matcher = PACKAGE_PATTERN.matcher(code);
+ String pkg;
+ if (matcher.find()) {
+ pkg = matcher.group(1);
+ } else {
+ pkg = "";
+ }
+ matcher = CLASS_PATTERN.matcher(code);
+ String cls;
+ if (matcher.find()) {
+ cls = matcher.group(1);
+ } else {
+ throw new IllegalArgumentException("No such class name in " + code);
+ }
+ String className = pkg != null && pkg.length() > 0 ? pkg + "." + cls : cls;
+ try {
+ return Class.forName(className, true, Thread.currentThread().getContextClassLoader());
+ } catch (ClassNotFoundException e) {
+ if (! code.endsWith("}")) {
+ throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n");
+ }
+ try {
+ return doCompile(className, code);
+ } catch (RuntimeException t) {
+ throw t;
+ } catch (Throwable t) {
+ throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(t));
+ }
+ }
+ }
+
+ protected abstract Class<?> doCompile(String name, String source) throws Throwable;
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AdaptiveCompiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AdaptiveCompiler.java
new file mode 100644
index 0000000..77b2bc7
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/AdaptiveCompiler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.compiler.support;
+
+
+import com.alibaba.dubbo.common.compiler.Compiler;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+
+/**
+ * AdaptiveCompiler. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Adaptive
+public class AdaptiveCompiler implements Compiler {
+
+ private static volatile String DEFAULT_COMPILER;
+
+ public static void setDefaultCompiler(String compiler) {
+ DEFAULT_COMPILER = compiler;
+ }
+
+ public Class<?> compile(String code, ClassLoader classLoader) {
+ Compiler compiler;
+ ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
+ String name = DEFAULT_COMPILER; // copy reference
+ if (name != null && name.length() > 0) {
+ compiler = loader.getExtension(name);
+ } else {
+ compiler = loader.getDefaultExtension();
+ }
+ return compiler.compile(code, classLoader);
+ }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/ClassUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/ClassUtils.java
new file mode 100644
index 0000000..c87e595
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/ClassUtils.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.compiler.support;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * ClassUtils. (Tool, Static, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class ClassUtils {
+
+ public static final String CLASS_EXTENSION = ".class";
+
+ public static final String JAVA_EXTENSION = ".java";
+
+ public static Object newInstance(String name) {
+ try {
+ return forName(name).newInstance();
+ } catch (InstantiationException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ public static Class<?> forName(String[] packages, String className) {
+ try {
+ return _forName(className);
+ } catch (ClassNotFoundException e) {
+ if (packages != null && packages.length > 0) {
+ for (String pkg : packages) {
+ try {
+ return _forName(pkg + "." + className);
+ } catch (ClassNotFoundException e2) {
+ }
+ }
+ }
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ public static Class<?> forName(String className) {
+ try {
+ return _forName(className);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ public static Class<?> _forName(String className) throws ClassNotFoundException {
+ if ("boolean".equals(className))
+ return boolean.class;
+ if ("byte".equals(className))
+ return byte.class;
+ if ("char".equals(className))
+ return char.class;
+ if ("short".equals(className))
+ return short.class;
+ if ("int".equals(className))
+ return int.class;
+ if ("long".equals(className))
+ return long.class;
+ if ("float".equals(className))
+ return float.class;
+ if ("double".equals(className))
+ return double.class;
+ if ("boolean[]".equals(className))
+ return boolean[].class;
+ if ("byte[]".equals(className))
+ return byte[].class;
+ if ("char[]".equals(className))
+ return char[].class;
+ if ("short[]".equals(className))
+ return short[].class;
+ if ("int[]".equals(className))
+ return int[].class;
+ if ("long[]".equals(className))
+ return long[].class;
+ if ("float[]".equals(className))
+ return float[].class;
+ if ("double[]".equals(className))
+ return double[].class;
+ try {
+ return arrayForName(className);
+ } catch (ClassNotFoundException e) {
+ if (className.indexOf('.') == -1) { // 尝试java.lang包
+ try {
+ return arrayForName("java.lang." + className);
+ } catch (ClassNotFoundException e2) {
+ // 忽略尝试异常, 抛出原始异常
+ }
+ }
+ throw e;
+ }
+ }
+
+ private static Class<?> arrayForName(String className) throws ClassNotFoundException {
+ return Class.forName(className.endsWith("[]")
+ ? "[L" + className.substring(0, className.length() - 2) + ";"
+ : className, true, Thread.currentThread().getContextClassLoader());
+ }
+
+ public static Class<?> getBoxedClass(Class<?> type) {
+ if (type == boolean.class) {
+ return Boolean.class;
+ } else if (type == char.class) {
+ return Character.class;
+ } else if (type == byte.class) {
+ return Byte.class;
+ } else if (type == short.class) {
+ return Short.class;
+ } else if (type == int.class) {
+ return Integer.class;
+ } else if (type == long.class) {
+ return Long.class;
+ } else if (type == float.class) {
+ return Float.class;
+ } else if (type == double.class) {
+ return Double.class;
+ } else {
+ return type;
+ }
+ }
+
+ public static Boolean boxed(boolean v) {
+ return Boolean.valueOf(v);
+ }
+
+ public static Character boxed(char v) {
+ return Character.valueOf(v);
+ }
+
+ public static Byte boxed(byte v) {
+ return Byte.valueOf(v);
+ }
+
+ public static Short boxed(short v) {
+ return Short.valueOf(v);
+ }
+
+ public static Integer boxed(int v) {
+ return Integer.valueOf(v);
+ }
+
+ public static Long boxed(long v) {
+ return Long.valueOf(v);
+ }
+
+ public static Float boxed(float v) {
+ return Float.valueOf(v);
+ }
+
+ public static Double boxed(double v) {
+ return Double.valueOf(v);
+ }
+
+ public static Object boxed(Object v) {
+ return v;
+ }
+
+ public static boolean unboxed(Boolean v) {
+ return v == null ? false : v.booleanValue();
+ }
+
+ public static char unboxed(Character v) {
+ return v == null ? '\0' : v.charValue();
+ }
+
+ public static byte unboxed(Byte v) {
+ return v == null ? 0 : v.byteValue();
+ }
+
+ public static short unboxed(Short v) {
+ return v == null ? 0 : v.shortValue();
+ }
+
+ public static int unboxed(Integer v) {
+ return v == null ? 0 : v.intValue();
+ }
+
+ public static long unboxed(Long v) {
+ return v == null ? 0 : v.longValue();
+ }
+
+ public static float unboxed(Float v) {
+ return v == null ? 0 : v.floatValue();
+ }
+
+ public static double unboxed(Double v) {
+ return v == null ? 0 : v.doubleValue();
+ }
+
+ public static Object unboxed(Object v) {
+ return v;
+ }
+
+ public static boolean isNotEmpty(Object object) {
+ return getSize(object) > 0;
+ }
+
+ public static int getSize(Object object) {
+ if (object == null) {
+ return 0;
+ } if (object instanceof Collection<?>) {
+ return ((Collection<?>)object).size();
+ } else if (object instanceof Map<?, ?>) {
+ return ((Map<?, ?>)object).size();
+ } else if (object.getClass().isArray()) {
+ return Array.getLength(object);
+ } else {
+ return -1;
+ }
+ }
+
+ public static URI toURI(String name) {
+ try {
+ return new URI(name);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Class<?> getGenericClass(Class<?> cls) {
+ return getGenericClass(cls, 0);
+ }
+
+ public static Class<?> getGenericClass(Class<?> cls, int i) {
+ try {
+ ParameterizedType parameterizedType = ((ParameterizedType) cls.getGenericInterfaces()[0]);
+ Object genericClass = parameterizedType.getActualTypeArguments()[i];
+ if (genericClass instanceof ParameterizedType) { // 处理多级泛型
+ return (Class<?>) ((ParameterizedType) genericClass).getRawType();
+ } else if (genericClass instanceof GenericArrayType) { // 处理数组泛型
+ return (Class<?>) ((GenericArrayType) genericClass).getGenericComponentType();
+ } else if (genericClass != null) {
+ return (Class<?>) genericClass;
+ }
+ } catch (Throwable e) {
+ }
+ if (cls.getSuperclass() != null) {
+ return getGenericClass(cls.getSuperclass(), i);
+ } else {
+ throw new IllegalArgumentException(cls.getName() + " generic type undefined!");
+ }
+ }
+
+ public static boolean isBeforeJava5(String javaVersion) {
+ return (javaVersion == null || javaVersion.length() == 0 || "1.0".equals(javaVersion)
+ || "1.1".equals(javaVersion) || "1.2".equals(javaVersion)
+ || "1.3".equals(javaVersion) || "1.4".equals(javaVersion));
+ }
+
+ public static boolean isBeforeJava6(String javaVersion) {
+ return isBeforeJava5(javaVersion) || "1.5".equals(javaVersion);
+ }
+
+ public static String toString(Throwable e) {
+ StringWriter w = new StringWriter();
+ PrintWriter p = new PrintWriter(w);
+ p.print(e.getClass().getName() + ": ");
+ if (e.getMessage() != null) {
+ p.print(e.getMessage() + "\n");
+ }
+ p.println();
+ try {
+ e.printStackTrace(p);
+ return w.toString();
+ } finally {
+ p.close();
+ }
+ }
+
+ private static final int JIT_LIMIT = 5 * 1024;
+
+ public static void checkBytecode(String name, byte[] bytecode) {
+ if (bytecode.length > JIT_LIMIT) {
+ System.err.println("The template bytecode too long, may be affect the JIT compiler. template class: " + name);
+ }
+ }
+
+ public static String getSizeMethod(Class<?> cls) {
+ try {
+ return cls.getMethod("size", new Class<?>[0]).getName() + "()";
+ } catch (NoSuchMethodException e) {
+ try {
+ return cls.getMethod("length", new Class<?>[0]).getName() + "()";
+ } catch (NoSuchMethodException e2) {
+ try {
+ return cls.getMethod("getSize", new Class<?>[0]).getName() + "()";
+ } catch (NoSuchMethodException e3) {
+ try {
+ return cls.getMethod("getLength", new Class<?>[0]).getName() + "()";
+ } catch (NoSuchMethodException e4) {
+ return null;
+ }
+ }
+ }
+ }
+ }
+
+ public static String getMethodName(Method method, Class<?>[] parameterClasses, String rightCode) {
+ if (method.getParameterTypes().length > parameterClasses.length) {
+ Class<?>[] types = method.getParameterTypes();
+ StringBuilder buf = new StringBuilder(rightCode);
+ for (int i = parameterClasses.length; i < types.length; i ++) {
+ if (buf.length() > 0) {
+ buf.append(",");
+ }
+ Class<?> type = types[i];
+ String def;
+ if (type == boolean.class) {
+ def = "false";
+ } else if (type == char.class) {
+ def = "\'\\0\'";
+ } else if (type == byte.class
+ || type == short.class
+ || type == int.class
+ || type == long.class
+ || type == float.class
+ || type == double.class) {
+ def = "0";
+ } else {
+ def = "null";
+ }
+ buf.append(def);
+ }
+ }
+ return method.getName() + "(" + rightCode + ")";
+ }
+
+ public static Method searchMethod(Class<?> currentClass, String name, Class<?>[] parameterTypes) throws NoSuchMethodException {
+ if (currentClass == null) {
+ throw new NoSuchMethodException("class == null");
+ }
+ try {
+ return currentClass.getMethod(name, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ for (Method method : currentClass.getMethods()) {
+ if (method.getName().equals(name)
+ && parameterTypes.length == method.getParameterTypes().length
+ && Modifier.isPublic(method.getModifiers())) {
+ if (parameterTypes.length > 0) {
+ Class<?>[] types = method.getParameterTypes();
+ boolean match = true;
+ for (int i = 0; i < parameterTypes.length; i ++) {
+ if (! types[i].isAssignableFrom(parameterTypes[i])) {
+ match = false;
+ break;
+ }
+ }
+ if (! match) {
+ continue;
+ }
+ }
+ return method;
+ }
+ }
+ throw e;
+ }
+ }
+
+ public static String getInitCode(Class<?> type) {
+ if (byte.class.equals(type)
+ || short.class.equals(type)
+ || int.class.equals(type)
+ || long.class.equals(type)
+ || float.class.equals(type)
+ || double.class.equals(type)) {
+ return "0";
+ } else if (char.class.equals(type)) {
+ return "'\\0'";
+ } else if (boolean.class.equals(type)) {
+ return "false";
+ } else {
+ return "null";
+ }
+ }
+
+ public static <K, V> Map<K, V> toMap(Map.Entry<K, V>[] entries) {
+ Map<K, V> map = new HashMap<K, V>();
+ if (entries != null && entries.length > 0) {
+ for (Map.Entry<K, V> enrty : entries) {
+ map.put(enrty.getKey(), enrty.getValue());
+ }
+ }
+ return map;
+ }
+
+ private ClassUtils() {}
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JavassistCompiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JavassistCompiler.java
new file mode 100644
index 0000000..49909d4
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JavassistCompiler.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.compiler.support;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.LoaderClassPath;
+
+/**
+ * JavassistCompiler. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class JavassistCompiler extends AbstractCompiler {
+
+ private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n");
+
+ private static final Pattern EXTENDS_PATTERN = Pattern.compile("\\s+extends\\s+([\\w\\.]+)[^\\{]*\\{\n");
+
+ private static final Pattern IMPLEMENTS_PATTERN = Pattern.compile("\\s+implements\\s+([\\w\\.]+)\\s*\\{\n");
+
+ private static final Pattern METHODS_PATTERN = Pattern.compile("\n(private|public|protected)\\s+");
+
+ private static final Pattern FIELD_PATTERN = Pattern.compile("[^\n]+=[^\n]+;");
+
+ @Override
+ public Class<?> doCompile(String name, String source) throws Throwable {
+ int i = name.lastIndexOf('.');
+ String className = i < 0 ? name : name.substring(i + 1);
+ ClassPool pool = new ClassPool(true);
+ pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
+ Matcher matcher = IMPORT_PATTERN.matcher(source);
+ List<String> importPackages = new ArrayList<String>();
+ Map<String, String> fullNames = new HashMap<String, String>();
+ while (matcher.find()) {
+ String pkg = matcher.group(1);
+ if (pkg.endsWith(".*")) {
+ String pkgName = pkg.substring(0, pkg.length() - 2);
+ pool.importPackage(pkgName);
+ importPackages.add(pkgName);
+ } else {
+ pool.importPackage(pkg);
+ int pi = pkg.lastIndexOf('.');
+ fullNames.put(pi < 0 ? pkg : pkg.substring(pi + 1), pkg);
+ }
+ }
+ String[] packages = importPackages.toArray(new String[0]);
+ matcher = EXTENDS_PATTERN.matcher(source);
+ CtClass cls;
+ if (matcher.find()) {
+ String extend = matcher.group(1).trim();
+ String extendClass;
+ if (extend.contains(".")) {
+ extendClass = extend;
+ } else if (fullNames.containsKey(extend)) {
+ extendClass = fullNames.get(extend);
+ } else {
+ extendClass = ClassUtils.forName(packages, extend).getName();
+ }
+ cls = pool.makeClass(name, pool.get(extendClass));
+ } else {
+ cls = pool.makeClass(name);
+ }
+ matcher = IMPLEMENTS_PATTERN.matcher(source);
+ if (matcher.find()) {
+ String[] ifaces = matcher.group(1).trim().split("\\,");
+ for (String iface : ifaces) {
+ iface = iface.trim();
+ String ifaceClass;
+ if (iface.contains(".")) {
+ ifaceClass = iface;
+ } else if (fullNames.containsKey(iface)) {
+ ifaceClass = fullNames.get(iface);
+ } else {
+ ifaceClass = ClassUtils.forName(packages, iface).getName();
+ }
+ cls.addInterface(pool.get(ifaceClass));
+ }
+ }
+ String body = source.substring(source.indexOf("{") + 1, source.length() - 1);
+ String[] methods = METHODS_PATTERN.split(body);
+ for (String method : methods) {
+ method = method.trim();
+ if (method.length() > 0) {
+ if (method.startsWith(className)) {
+ cls.addConstructor(CtNewConstructor.make("public " + method, cls));
+ } else if (FIELD_PATTERN.matcher(method).matches()) {
+ cls.addField(CtField.make("private " + method, cls));
+ } else {
+ cls.addMethod(CtNewMethod.make("public " + method, cls));
+ }
+ }
+ }
+ return cls.toClass();
+ }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JdkCompiler.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JdkCompiler.java
new file mode 100644
index 0000000..4d0a558
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/compiler/support/JdkCompiler.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.compiler.support;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.tools.DiagnosticCollector;
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+/**
+ * JdkCompiler. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class JdkCompiler extends AbstractCompiler {
+
+ private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+
+ private final DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
+
+ private final ClassLoaderImpl classLoader;
+
+ private final JavaFileManagerImpl javaFileManager;
+
+ private volatile List<String> options;
+
+ public JdkCompiler(){
+ options = new ArrayList<String>();
+ options.add("-target");
+ options.add("1.6");
+ StandardJavaFileManager manager = compiler.getStandardFileManager(diagnosticCollector, null, null);
+ final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if (loader instanceof URLClassLoader
+ && (! loader.getClass().getName().equals("sun.misc.Launcher$AppClassLoader"))) {
+ try {
+ URLClassLoader urlClassLoader = (URLClassLoader) loader;
+ List<File> files = new ArrayList<File>();
+ for (URL url : urlClassLoader.getURLs()) {
+ files.add(new File(url.getFile()));
+ }
+ manager.setLocation(StandardLocation.CLASS_PATH, files);
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ classLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoaderImpl>() {
+ public ClassLoaderImpl run() {
+ return new ClassLoaderImpl(loader);
+ }
+ });
+ javaFileManager = new JavaFileManagerImpl(manager, classLoader);
+ }
+
+ @Override
+ public Class<?> doCompile(String name, String sourceCode) throws Throwable {
+ int i = name.lastIndexOf('.');
+ String packageName = i < 0 ? "" : name.substring(0, i);
+ String className = i < 0 ? name : name.substring(i + 1);
+ JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode);
+ javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName,
+ className + ClassUtils.JAVA_EXTENSION, javaFileObject);
+ Boolean result = compiler.getTask(null, javaFileManager, diagnosticCollector, options,
+ null, Arrays.asList(new JavaFileObject[]{javaFileObject})).call();
+ if (result == null || ! result.booleanValue()) {
+ throw new IllegalStateException("Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector);
+ }
+ return classLoader.loadClass(name);
+ }
+
+ private final class ClassLoaderImpl extends ClassLoader {
+
+ private final Map<String, JavaFileObject> classes = new HashMap<String, JavaFileObject>();
+
+ ClassLoaderImpl(final ClassLoader parentClassLoader) {
+ super(parentClassLoader);
+ }
+
+ Collection<JavaFileObject> files() {
+ return Collections.unmodifiableCollection(classes.values());
+ }
+
+ @Override
+ protected Class<?> findClass(final String qualifiedClassName) throws ClassNotFoundException {
+ JavaFileObject file = classes.get(qualifiedClassName);
+ if (file != null) {
+ byte[] bytes = ((JavaFileObjectImpl) file).getByteCode();
+ return defineClass(qualifiedClassName, bytes, 0, bytes.length);
+ }
+ try {
+ return Class.forName(qualifiedClassName);
+ } catch (ClassNotFoundException nf) {
+ return super.findClass(qualifiedClassName);
+ }
+ }
+
+ void add(final String qualifiedClassName, final JavaFileObject javaFile) {
+ classes.put(qualifiedClassName, javaFile);
+ }
+
+ @Override
+ protected synchronized Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ public InputStream getResourceAsStream(final String name) {
+ if (name.endsWith(ClassUtils.CLASS_EXTENSION)) {
+ String qualifiedClassName = name.substring(0, name.length() - ClassUtils.CLASS_EXTENSION.length()).replace('/', '.');
+ JavaFileObjectImpl file = (JavaFileObjectImpl) classes.get(qualifiedClassName);
+ if (file != null) {
+ return new ByteArrayInputStream(file.getByteCode());
+ }
+ }
+ return super.getResourceAsStream(name);
+ }
+ }
+
+ private static final class JavaFileObjectImpl extends SimpleJavaFileObject {
+
+ private ByteArrayOutputStream bytecode;
+
+ private final CharSequence source;
+
+ public JavaFileObjectImpl(final String baseName, final CharSequence source){
+ super(ClassUtils.toURI(baseName + ClassUtils.JAVA_EXTENSION), Kind.SOURCE);
+ this.source = source;
+ }
+
+ JavaFileObjectImpl(final String name, final Kind kind){
+ super(ClassUtils.toURI(name), kind);
+ source = null;
+ }
+
+ public JavaFileObjectImpl(URI uri, Kind kind){
+ super(uri, kind);
+ source = null;
+ }
+
+ @Override
+ public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws UnsupportedOperationException {
+ if (source == null) {
+ throw new UnsupportedOperationException("source == null");
+ }
+ return source;
+ }
+
+ @Override
+ public InputStream openInputStream() {
+ return new ByteArrayInputStream(getByteCode());
+ }
+
+ @Override
+ public OutputStream openOutputStream() {
+ return bytecode = new ByteArrayOutputStream();
+ }
+
+ public byte[] getByteCode() {
+ return bytecode.toByteArray();
+ }
+ }
+
+ private static final class JavaFileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> {
+
+ private final ClassLoaderImpl classLoader;
+
+ private final Map<URI, JavaFileObject> fileObjects = new HashMap<URI, JavaFileObject>();
+
+ public JavaFileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) {
+ super(fileManager);
+ this.classLoader = classLoader;
+ }
+
+ @Override
+ public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
+ FileObject o = fileObjects.get(uri(location, packageName, relativeName));
+ if (o != null)
+ return o;
+ return super.getFileForInput(location, packageName, relativeName);
+ }
+
+ public void putFileForInput(StandardLocation location, String packageName, String relativeName, JavaFileObject file) {
+ fileObjects.put(uri(location, packageName, relativeName), file);
+ }
+
+ private URI uri(Location location, String packageName, String relativeName) {
+ return ClassUtils.toURI(location.getName() + '/' + packageName + '/' + relativeName);
+ }
+
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, Kind kind, FileObject outputFile)
+ throws IOException {
+ JavaFileObject file = new JavaFileObjectImpl(qualifiedName, kind);
+ classLoader.add(qualifiedName, file);
+ return file;
+ }
+
+ @Override
+ public ClassLoader getClassLoader(JavaFileManager.Location location) {
+ return classLoader;
+ }
+
+ @Override
+ public String inferBinaryName(Location loc, JavaFileObject file) {
+ if (file instanceof JavaFileObjectImpl)
+ return file.getName();
+ return super.inferBinaryName(loc, file);
+ }
+
+ @Override
+ public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse)
+ throws IOException {
+ Iterable<JavaFileObject> result = super.list(location, packageName, kinds, recurse);
+
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ List<URL> urlList = new ArrayList<URL>();
+ Enumeration<URL> e = contextClassLoader.getResources("com");
+ while (e.hasMoreElements()) {
+ urlList.add(e.nextElement());
+ }
+
+ ArrayList<JavaFileObject> files = new ArrayList<JavaFileObject>();
+
+ if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {
+ for (JavaFileObject file : fileObjects.values()) {
+ if (file.getKind() == Kind.CLASS && file.getName().startsWith(packageName)) {
+ files.add(file);
+ }
+ }
+
+ files.addAll(classLoader.files());
+ } else if (location == StandardLocation.SOURCE_PATH && kinds.contains(JavaFileObject.Kind.SOURCE)) {
+ for (JavaFileObject file : fileObjects.values()) {
+ if (file.getKind() == Kind.SOURCE && file.getName().startsWith(packageName)) {
+ files.add(file);
+ }
+ }
+ }
+
+ for (JavaFileObject file : result) {
+ files.add(file);
+ }
+
+ return files;
+ }
+ }
+
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Activate.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Activate.java
new file mode 100644
index 0000000..ceb694e
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Activate.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extension;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Activate.
+ *
+ * @author william.liangf
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+public @interface Activate {
+
+ String[] group() default {};
+
+ String[] value() default {};
+
+ String[] before() default {};
+
+ String[] after() default {};
+
+ int order() default 0;
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Adaptive.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Adaptive.java
new file mode 100644
index 0000000..7f42c5a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/Adaptive.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extension;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * 在{@link ExtensionLoader}生成Extension的Adaptive Instance时,为
+ * {@link ExtensionLoader}提供信息。
+ *
+ * @author ding.lid
+ *
+ * @see ExtensionLoader
+ * @see URL
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+public @interface Adaptive {
+
+ /**
+ * 从{@link URL}的Key名,对应的Value作为要Adapt成的Extension名。
+ * <p>
+ * 如果{@link URL}这些Key都没有Value,使用 用 缺省的扩展(在接口的{@link SPI}中设定的值)。<br>
+ * 比如,<code>String[] {"key1", "key2"}</code>,表示
+ * <ol>
+ * <li>先在URL上找key1的Value作为要Adapt成的Extension名;
+ * <li>key1没有Value,则使用key2的Value作为要Adapt成的Extension名。
+ * <li>key2没有Value,使用缺省的扩展。
+ * <li>如果没有设定缺省扩展,则方法调用会抛出{@link IllegalStateException}。
+ * </ol>
+ * <p>
+ * 如果不设置则缺省使用Extension接口类名的点分隔小写字串。<br>
+ * 即对于Extension接口{@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}的缺省值为
+ * <code>String[] {"yyy.invoker.wrapper"}</code>
+ *
+ * @see SPI#value()
+ */
+ String[] value() default {};
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionFactory.java
new file mode 100644
index 0000000..73949b9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extension;
+
+/**
+ * ExtensionFactory
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface ExtensionFactory {
+
+ /**
+ * Get extension.
+ *
+ * @param type object type.
+ * @param name object name.
+ * @return object instance.
+ */
+ <T> T getExtension(Class<T> type, String name);
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionLoader.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionLoader.java
new file mode 100644
index 0000000..8508f28
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/ExtensionLoader.java
@@ -0,0 +1,849 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extension;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.support.ActivateComparator;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.Reference;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+/**
+ * Dubbo使用的扩展点获取。<p>
+ * <ul>
+ * <li>自动注入关联扩展点。</li>
+ * <li>自动Wrap上扩展点的Wrap类。</li>
+ * <li>缺省获得的的扩展点是一个Adaptive Instance。
+ * </ul>
+ *
+ * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider">JDK5.0的自动发现机制实现</a>
+ *
+ * @author william.liangf
+ * @author ding.lid
+ *
+ * @see Adaptive
+ */
+public class ExtensionLoader<T> {
+
+ private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
+
+ private static final String SERVICES_DIRECTORY = "META-INF/services/";
+
+ private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
+
+ private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
+
+ private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
+
+ private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
+
+ private final Class<?> type;
+
+ private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
+
+ private final Reference<Map<String, Class<?>>> cachedClasses = new Reference<Map<String,Class<?>>>();
+
+ private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
+
+ private final ConcurrentMap<String, Reference<Object>> cachedInstances = new ConcurrentHashMap<String, Reference<Object>>();
+
+ private volatile Class<?> cachedAdaptiveClass = null;
+
+ private final Reference<Object> cachedAdaptiveInstance = new Reference<Object>();
+ private volatile Throwable createAdaptiveInstanceError;
+
+ private Set<Class<?>> cachedWrapperClasses;
+
+ private String cachedDefaultName;
+
+ private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
+
+ private final ExtensionFactory objectFactory;
+
+ private static <T> boolean withExtensionAnnotation(Class<T> type) {
+ return type.isAnnotationPresent(SPI.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
+ if (type == null)
+ throw new IllegalArgumentException("Extension type == null");
+ if(!withExtensionAnnotation(type)) {
+ throw new IllegalArgumentException("Extension type(" + type +
+ ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
+ }
+
+ ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
+ if (loader == null) {
+ EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
+ loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
+ }
+ return loader;
+ }
+
+ private ExtensionLoader(Class<?> type) {
+ this.type = type;
+ objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
+ }
+
+ public String getExtensionName(T extensionInstance) {
+ return getExtensionName(extensionInstance.getClass());
+ }
+
+ public String getExtensionName(Class<?> extensionClass) {
+ return cachedNames.get(extensionClass);
+ }
+
+ /**
+ * This is equivalent to <pre>
+ * getActivateExtension(url, key, null);
+ * </pre>
+ *
+ * @see #getActivateExtension(com.alibaba.dubbo.common.URL, String, String)
+ * @param url url
+ * @param key url parameter key which used to get extension point names
+ * @return extension list which are activated.
+ */
+ public List<T> getActivateExtension(URL url, String key) {
+ return getActivateExtension(url, key, null);
+ }
+
+ /**
+ * This is equivalent to <pre>
+ * getActivateExtension(url, values, null);
+ * </pre>
+ *
+ * @see #getActivateExtension(com.alibaba.dubbo.common.URL, String[], String)
+ * @param url url
+ * @param values extension point names
+ * @return extension list which are activated
+ */
+ public List<T> getActivateExtension(URL url, String[] values) {
+ return getActivateExtension(url, values, null);
+ }
+
+ /**
+ * This is equivalent to <pre>
+ * getActivateExtension(url, url.getParameter(key).split(","), null);
+ * </pre>
+ *
+ * @see #getActivateExtension(com.alibaba.dubbo.common.URL, String[], String)
+ * @param url url
+ * @param key url parameter key which used to get extension point names
+ * @param group group
+ * @return extension list which are activated.
+ */
+ public List<T> getActivateExtension(URL url, String key, String group) {
+ String value = url.getParameter(key);
+ return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
+ }
+
+ /**
+ * Get activate extensions.
+ *
+ * @see Activate
+ * @param url url
+ * @param values extension point names
+ * @param group group
+ * @return extension list which are activated
+ */
+ public List<T> getActivateExtension(URL url, String[] values, String group) {
+ List<T> exts = new ArrayList<T>();
+ List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
+ if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
+ getExtensionClasses();
+ for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
+ String name = entry.getKey();
+ Activate activate = entry.getValue();
+ if (isMatchGroup(group, activate.group())) {
+ T ext = getExtension(name);
+ if (! names.contains(name)
+ && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)
+ && isActive(activate, url)) {
+ exts.add(ext);
+ }
+ }
+ }
+ Collections.sort(exts, ActivateComparator.COMPARATOR);
+ }
+ List<T> usrs = new ArrayList<T>();
+ for (int i = 0; i < names.size(); i ++) {
+ String name = names.get(i);
+ if (! name.startsWith(Constants.REMOVE_VALUE_PREFIX)
+ && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
+ if (Constants.DEFAULT_KEY.equals(name)) {
+ if (usrs.size() > 0) {
+ exts.addAll(0, usrs);
+ usrs.clear();
+ }
+ } else {
+ T ext = getExtension(name);
+ usrs.add(ext);
+ }
+ }
+ }
+ if (usrs.size() > 0) {
+ exts.addAll(usrs);
+ }
+ return exts;
+ }
+
+ private boolean isMatchGroup(String group, String[] groups) {
+ if (group == null || group.length() == 0) {
+ return true;
+ }
+ if (groups != null && groups.length > 0) {
+ for (String g : groups) {
+ if (group.equals(g)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isActive(Activate activate, URL url) {
+ String[] keys = activate.value();
+ if (keys == null || keys.length == 0) {
+ return true;
+ }
+ for (String key : keys) {
+ for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
+ String k = entry.getKey();
+ String v = entry.getValue();
+ if ((k.equals(key) || k.endsWith("." + key))
+ && ConfigUtils.isNotEmpty(v)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getLoadedExtension(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);
+ }
+ return (T) reference.get();
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getExtension(String name) {
+ if (name == null || name.length() == 0)
+ throw new IllegalArgumentException("Extension name == null");
+ if ("true".equals(name)) {
+ return getDefaultExtension();
+ }
+ Reference<Object> reference = cachedInstances.get(name);
+ if (reference == null) {
+ cachedInstances.putIfAbsent(name, new Reference<Object>());
+ reference = cachedInstances.get(name);
+ }
+ Object instance = reference.get();
+ if (instance == null) {
+ synchronized (reference) {
+ instance = reference.get();
+ if (instance == null) {
+ instance = createExtension(name);
+ reference.set(instance);
+ }
+ }
+ }
+ return (T) instance;
+ }
+
+ /**
+ * 返回缺省的扩展,如果没有设置则返回<code>null</code>。
+ */
+ public T getDefaultExtension() {
+ getExtensionClasses();
+ if(null == cachedDefaultName || cachedDefaultName.length() == 0
+ || "true".equals(cachedDefaultName)) {
+ return null;
+ }
+ return getExtension(cachedDefaultName);
+ }
+
+ public boolean hasExtension(String name) {
+ if (name == null || name.length() == 0)
+ throw new IllegalArgumentException("Extension name == null");
+ try {
+ return getExtensionClass(name) != null;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ public Set<String> getSupportedExtensions() {
+ Map<String, Class<?>> clazzes = getExtensionClasses();
+ return Collections.unmodifiableSet(new TreeSet<String>(clazzes.keySet()));
+ }
+
+ public Set<String> getLoadedExtensions() {
+ return Collections.unmodifiableSet(new TreeSet<String>(cachedInstances.keySet()));
+ }
+
+ /**
+ * 返回缺省的扩展点名,如果没有设置缺省则返回<code>null</code>。
+ */
+ public String getDefaultExtensionName() {
+ getExtensionClasses();
+ return cachedDefaultName;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public T getAdaptiveExtension() {
+ Object instance = cachedAdaptiveInstance.get();
+ if (instance == null) {
+ if(createAdaptiveInstanceError == null) {
+ synchronized (cachedAdaptiveInstance) {
+ instance = cachedAdaptiveInstance.get();
+ if (instance == null) {
+ try {
+ instance = createAdaptiveExtension();
+ cachedAdaptiveInstance.set(instance);
+ } catch (Throwable t) {
+ createAdaptiveInstanceError = t;
+ rethrowAsRuntime(t, "fail to create adaptive instance: ");
+ }
+ }
+ }
+ }
+ else {
+ rethrowAsRuntime(createAdaptiveInstanceError, "fail to create adaptive instance: ");
+ }
+ }
+
+ return (T) instance;
+ }
+
+ private static void rethrowAsRuntime(Throwable t, String message) {
+ if(t instanceof RuntimeException)
+ throw (RuntimeException)t;
+ else
+ throw new IllegalStateException(message + t.toString(), t);
+ }
+
+ private IllegalStateException findException(String name) {
+ for (Map.Entry<String, IllegalStateException> entry : exceptions.entrySet()) {
+ if (entry.getKey().toLowerCase().contains(name.toLowerCase())) {
+ return entry.getValue();
+ }
+ }
+ StringBuilder buf = new StringBuilder("No such extension " + type.getName() + " by name " + name + ", possible causes: ");
+ int i = 1;
+ for (Map.Entry<String, IllegalStateException> entry : exceptions.entrySet()) {
+ buf.append("\r\n(");
+ buf.append(i ++);
+ buf.append(") ");
+ buf.append(entry.getKey());
+ buf.append(":\r\n");
+ buf.append(StringUtils.toString(entry.getValue()));
+ }
+ return new IllegalStateException(buf.toString());
+ }
+
+ @SuppressWarnings("unchecked")
+ private T createExtension(String name) {
+ Class<?> clazz = getExtensionClasses().get(name);
+ if (clazz == null) {
+ throw findException(name);
+ }
+ try {
+ T instance = (T) EXTENSION_INSTANCES.get(clazz);
+ if (instance == null) {
+ EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
+ instance = (T) EXTENSION_INSTANCES.get(clazz);
+ }
+ injectExtension(instance);
+ Set<Class<?>> wrapperClasses = cachedWrapperClasses;
+ if (wrapperClasses != null && wrapperClasses.size() > 0) {
+ for (Class<?> wrapperClass : wrapperClasses) {
+ instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
+ }
+ }
+ return instance;
+ } catch (Throwable t) {
+ throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
+ type + ") could not be instantiated: " + t.getMessage(), t);
+ }
+ }
+
+ private T injectExtension(T instance) {
+ try {
+ if (objectFactory != null) {
+ for (Method method : instance.getClass().getMethods()) {
+ if (method.getName().startsWith("set")
+ && method.getParameterTypes().length == 1
+ && Modifier.isPublic(method.getModifiers())) {
+ Class<?> pt = method.getParameterTypes()[0];
+ try {
+ String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
+ Object object = objectFactory.getExtension(pt, property);
+ if (object != null) {
+ method.invoke(instance, object);
+ }
+ } catch (Exception e) {
+ logger.error("fail to inject via method " + method.getName()
+ + " of interface " + type.getName() + ": " + e.getMessage(), e);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ return instance;
+ }
+
+ private Class<?> getExtensionClass(String name) {
+ if (type == null)
+ throw new IllegalArgumentException("Extension type == null");
+ if (name == null)
+ throw new IllegalArgumentException("Extension name == null");
+ Class<?> clazz = getExtensionClasses().get(name);
+ if (clazz == null)
+ throw new IllegalStateException("No such extension \"" + name + "\" for " + type.getName() + "!");
+ return clazz;
+ }
+
+ private Map<String, Class<?>> getExtensionClasses() {
+ Map<String, Class<?>> classes = cachedClasses.get();
+ if (classes == null) {
+ synchronized (cachedClasses) {
+ classes = cachedClasses.get();
+ if (classes == null) {
+ classes = loadExtensionClasses();
+ cachedClasses.set(classes);
+ }
+ }
+ }
+ return classes;
+ }
+
+ private Map<String, Class<?>> loadExtensionClasses() {
+ final SPI defaultAnnotation = type.getAnnotation(SPI.class);
+ if(defaultAnnotation != null) {
+ String value = defaultAnnotation.value();
+ if(value != null && (value = value.trim()).length() > 0) {
+ String[] names = NAME_SEPARATOR.split(value);
+ if(names.length > 1) {
+ throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ + ": " + Arrays.toString(names));
+ }
+ if(names.length == 1) cachedDefaultName = names[0];
+ }
+ }
+
+ Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
+ loadFile(extensionClasses, DUBBO_DIRECTORY);
+ loadFile(extensionClasses, SERVICES_DIRECTORY);
+ return extensionClasses;
+ }
+
+ private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
+ String fileName = dir + type.getName();
+ try {
+ Enumeration<java.net.URL> urls;
+ ClassLoader classLoader = findClassLoader();
+ if (classLoader != null) {
+ urls = classLoader.getResources(fileName);
+ } else {
+ urls = ClassLoader.getSystemResources(fileName);
+ }
+ if (urls != null) {
+ while (urls.hasMoreElements()) {
+ java.net.URL url = urls.nextElement();
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
+ try {
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ final int ci = line.indexOf('#');
+ if (ci >= 0) line = line.substring(0, ci);
+ line = line.trim();
+ if (line.length() > 0) {
+ try {
+ String name = null;
+ int i = line.indexOf('=');
+ if (i > 0) {
+ name = line.substring(0, i).trim();
+ line = line.substring(i + 1).trim();
+ }
+ if (line.length() > 0) {
+ Class<?> clazz = Class.forName(line, true, classLoader);
+ if (! type.isAssignableFrom(clazz)) {
+ throw new IllegalStateException("Error when load extension class(interface: " +
+ type + ", class line: " + clazz.getName() + "), class "
+ + clazz.getName() + "is not subtype of interface.");
+ }
+ if (clazz.isAnnotationPresent(Adaptive.class)) {
+ if(cachedAdaptiveClass == null) {
+ cachedAdaptiveClass = clazz;
+ } else if (! cachedAdaptiveClass.equals(clazz)) {
+ throw new IllegalStateException("More than 1 adaptive class found: "
+ + cachedAdaptiveClass.getClass().getName()
+ + ", " + clazz.getClass().getName());
+ }
+ } else {
+ try {
+ clazz.getConstructor(type);
+ Set<Class<?>> wrappers = cachedWrapperClasses;
+ if (wrappers == null) {
+ cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
+ wrappers = cachedWrapperClasses;
+ }
+ wrappers.add(clazz);
+ } catch (NoSuchMethodException e) {
+ clazz.getConstructor();
+ if (name == null || name.length() == 0) {
+ name = findAnnotationName(clazz);
+ if (name == null || name.length() == 0) {
+ if (clazz.getSimpleName().length() > type.getSimpleName().length()
+ && clazz.getSimpleName().endsWith(type.getSimpleName())) {
+ name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
+ } else {
+ throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
+ }
+ }
+ }
+ String[] names = NAME_SEPARATOR.split(name);
+ if (names != null && names.length > 0) {
+ Activate activate = clazz.getAnnotation(Activate.class);
+ if (activate != null) {
+ cachedActivates.put(names[0], activate);
+ }
+ for (String n : names) {
+ if (! cachedNames.containsKey(clazz)) {
+ cachedNames.put(clazz, n);
+ }
+ Class<?> c = extensionClasses.get(n);
+ if (c == null) {
+ extensionClasses.put(n, clazz);
+ } else if (c != clazz) {
+ throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
+ exceptions.put(line, e);
+ }
+ }
+ } // end of while read lines
+ } finally {
+ reader.close();
+ }
+ } catch (Throwable t) {
+ logger.error("Exception when load extension class(interface: " +
+ type + ", class file: " + url + ") in " + url, t);
+ }
+ } // end of while urls
+ }
+ } catch (Throwable t) {
+ logger.error("Exception when load extension class(interface: " +
+ type + ", description file: " + fileName + ").", t);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private String findAnnotationName(Class<?> clazz) {
+ com.alibaba.dubbo.common.Extension extension = clazz.getAnnotation(com.alibaba.dubbo.common.Extension.class);
+ if (extension == null) {
+ String name = clazz.getSimpleName();
+ if (name.endsWith(type.getSimpleName())) {
+ name = name.substring(0, name.length() - type.getSimpleName().length());
+ }
+ return name.toLowerCase();
+ }
+ return extension.value();
+ }
+
+ @SuppressWarnings("unchecked")
+ private T createAdaptiveExtension() {
+ try {
+ return injectExtension((T) getAdaptiveExtensionClass().newInstance());
+ } catch (Exception e) {
+ throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ private Class<?> getAdaptiveExtensionClass() {
+ getExtensionClasses();
+ if (cachedAdaptiveClass != null) {
+ return cachedAdaptiveClass;
+ }
+ return cachedAdaptiveClass = createAdaptiveExtensionClass();
+ }
+
+ private Class<?> createAdaptiveExtensionClass() {
+ String code = createAdaptiveExtensionClassCode();
+ ClassLoader classLoader = findClassLoader();
+ com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
+ return compiler.compile(code, classLoader);
+ }
+
+ private String createAdaptiveExtensionClassCode() {
+ StringBuilder codeBuidler = new StringBuilder();
+ Method[] methods = type.getMethods();
+ boolean hasAdaptiveAnnotation = false;
+ for(Method m : methods) {
+ if(m.isAnnotationPresent(Adaptive.class)) {
+ hasAdaptiveAnnotation = true;
+ break;
+ }
+ }
+ // 完全没有Adaptive方法,则不需要生成Adaptive类
+ if(! hasAdaptiveAnnotation)
+ throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
+
+ codeBuidler.append("package " + type.getPackage().getName() + ";");
+ codeBuidler.append("\nimport " + ExtensionLoader.class.getPackage().getName() + ";");
+ codeBuidler.append("\npublic class " + type.getSimpleName() + "$Adpative" + " implements " + type.getCanonicalName() + " {");
+
+ for (Method method : methods) {
+ Class<?> rt = method.getReturnType();
+ Class<?>[] pts = method.getParameterTypes();
+ Class<?>[] ets = method.getExceptionTypes();
+
+ Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
+ StringBuilder code = new StringBuilder(512);
+ if (adaptiveAnnotation == null) {
+ code.append("throw new UnsupportedOperationException(\"method ")
+ .append(method.toString()).append(" of interface ")
+ .append(type.getName()).append(" is not adaptive method!\");");
+ } else {
+ int urlTypeIndex = -1;
+ for (int i = 0; i < pts.length; ++i) {
+ if (pts[i].equals(URL.class)) {
+ urlTypeIndex = i;
+ break;
+ }
+ }
+ // 有类型为URL的参数
+ if (urlTypeIndex != -1) {
+ // Null Point check
+ String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",
+ urlTypeIndex);
+ code.append(s);
+
+ s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex);
+ code.append(s);
+ }
+ // 参数没有URL类型
+ else {
+ String attribMethod = null;
+
+ // 找到参数的URL属性
+ LBL_PTS:
+ for (int i = 0; i < pts.length; ++i) {
+ Method[] ms = pts[i].getMethods();
+ for (Method m : ms) {
+ String name = m.getName();
+ if ((name.startsWith("get") || name.length() > 3)
+ && Modifier.isPublic(m.getModifiers())
+ && !Modifier.isStatic(m.getModifiers())
+ && m.getParameterTypes().length == 0
+ && m.getReturnType() == URL.class) {
+ urlTypeIndex = i;
+ attribMethod = name;
+ break LBL_PTS;
+ }
+ }
+ }
+ if(attribMethod == null) {
+ throw new IllegalStateException("fail to create adative class for interface " + type.getName()
+ + ": not found url parameter or url attribute in parameters of method " + method.getName());
+ }
+
+ // Null point check
+ String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",
+ urlTypeIndex, pts[urlTypeIndex].getName());
+ code.append(s);
+ s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",
+ urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);
+ code.append(s);
+
+ s = String.format("%s url = arg%d.%s();",URL.class.getName(), urlTypeIndex, attribMethod);
+ code.append(s);
+ }
+
+ String[] value = adaptiveAnnotation.value();
+ // 没有设置Key,则使用“扩展点接口名的点分隔 作为Key
+ if(value.length == 0) {
+ char[] charArray = type.getSimpleName().toCharArray();
+ StringBuilder sb = new StringBuilder(128);
+ for (int i = 0; i < charArray.length; i++) {
+ if(Character.isUpperCase(charArray[i])) {
+ if(i != 0) {
+ sb.append(".");
+ }
+ sb.append(Character.toLowerCase(charArray[i]));
+ }
+ else {
+ sb.append(charArray[i]);
+ }
+ }
+ value = new String[] {sb.toString()};
+ }
+
+ boolean hasInvocation = false;
+ for (int i = 0; i < pts.length; ++i) {
+ if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) {
+ // Null Point check
+ String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i);
+ code.append(s);
+ s = String.format("\nString methodName = arg%d.getMethodName();", i);
+ code.append(s);
+ hasInvocation = true;
+ break;
+ }
+ }
+
+ String defaultExtName = cachedDefaultName;
+ String getNameCode = null;
+ for (int i = value.length - 1; i >= 0; --i) {
+ if(i == value.length - 1) {
+ if(null != defaultExtName) {
+ if(!"protocol".equals(value[i]))
+ if (hasInvocation)
+ getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
+ else
+ getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
+ else
+ getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
+ }
+ else {
+ if(!"protocol".equals(value[i]))
+ if (hasInvocation)
+ getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
+ else
+ getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
+ else
+ getNameCode = "url.getProtocol()";
+ }
+ }
+ else {
+ if(!"protocol".equals(value[i]))
+ if (hasInvocation)
+ getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
+ else
+ getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
+ else
+ getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
+ }
+ }
+ code.append("\nString extName = ").append(getNameCode).append(";");
+ // check extName == null?
+ String s = String.format("\nif(extName == null) " +
+ "throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",
+ type.getName(), Arrays.toString(value));
+ code.append(s);
+
+ s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",
+ type.getName(), ExtensionLoader.class.getSimpleName(), type.getName());
+ code.append(s);
+
+ // return statement
+ if (!rt.equals(void.class)) {
+ code.append("\nreturn ");
+ }
+
+ s = String.format("extension.%s(", method.getName());
+ code.append(s);
+ for (int i = 0; i < pts.length; i++) {
+ if (i != 0)
+ code.append(", ");
+ code.append("arg").append(i);
+ }
+ code.append(");");
+ }
+
+ codeBuidler.append("\npublic " + rt.getCanonicalName() + " " + method.getName() + "(");
+ for (int i = 0; i < pts.length; i ++) {
+ if (i > 0) {
+ codeBuidler.append(", ");
+ }
+ codeBuidler.append(pts[i].getCanonicalName());
+ codeBuidler.append(" ");
+ codeBuidler.append("arg" + i);
+ }
+ codeBuidler.append(")");
+ if (ets.length > 0) {
+ codeBuidler.append(" throws ");
+ for (int i = 0; i < ets.length; i ++) {
+ if (i > 0) {
+ codeBuidler.append(", ");
+ }
+ codeBuidler.append(pts[i].getCanonicalName());
+ }
+ }
+ codeBuidler.append(" {");
+ codeBuidler.append(code.toString());
+ codeBuidler.append("\n}");
+ }
+ codeBuidler.append("\n}");
+ if (logger.isDebugEnabled()) {
+ logger.debug(codeBuidler.toString());
+ }
+ return codeBuidler.toString();
+ }
+
+ private static ClassLoader findClassLoader() {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ if (classLoader != null) {
+ return classLoader;
+ }
+ classLoader = ExtensionLoader.class.getClassLoader();
+ return classLoader;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + "[" + type.getName() + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/SPI.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/SPI.java
new file mode 100644
index 0000000..b9f4515
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/SPI.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extension;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 扩展点接口的标识。
+ *
+ * @author william.liangf
+ * @author ding.lid
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface SPI {
+
+ /**
+ * 缺省扩展点名。
+ */
+ String value() default "";
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/AdaptiveExtensionFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/AdaptiveExtensionFactory.java
new file mode 100644
index 0000000..bf67a6a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/AdaptiveExtensionFactory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extension.factory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.ExtensionFactory;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+
+/**
+ * AdaptiveExtensionFactory
+ *
+ * @author william.liangf
+ */
+@Adaptive
+public class AdaptiveExtensionFactory implements ExtensionFactory {
+
+ private final List<ExtensionFactory> factories;
+
+ public AdaptiveExtensionFactory() {
+ ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
+ List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
+ for (String name : loader.getSupportedExtensions()) {
+ list.add(loader.getExtension(name));
+ }
+ factories = Collections.unmodifiableList(list);
+ }
+
+ public <T> T getExtension(Class<T> type, String name) {
+ for (ExtensionFactory factory : factories) {
+ T extension = factory.getExtension(type, name);
+ if (extension != null) {
+ return extension;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/SpiExtensionFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/SpiExtensionFactory.java
new file mode 100644
index 0000000..c960a74
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/factory/SpiExtensionFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extension.factory;
+
+import com.alibaba.dubbo.common.extension.ExtensionFactory;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * SpiExtensionFactory
+ *
+ * @author william.liangf
+ */
+public class SpiExtensionFactory implements ExtensionFactory {
+
+ public <T> T getExtension(Class<T> type, String name) {
+ if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
+ ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
+ if (loader.getSupportedExtensions().size() > 0) {
+ return loader.getAdaptiveExtension();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/support/ActivateComparator.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/support/ActivateComparator.java
new file mode 100644
index 0000000..16351fb
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/extension/support/ActivateComparator.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extension.support;
+
+import java.util.Comparator;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * OrderComparetor
+ *
+ * @author william.liangf
+ */
+public class ActivateComparator implements Comparator<Object> {
+
+ public static final Comparator<Object> COMPARATOR = new ActivateComparator();
+
+ public int compare(Object o1, Object o2) {
+ if (o1 == null && o2 == null) {
+ return 0;
+ }
+ if (o1 == null) {
+ return -1;
+ }
+ if (o2 == null) {
+ return 1;
+ }
+ if (o1.equals(o2)) {
+ return 0;
+ }
+ Activate a1 = o1.getClass().getAnnotation(Activate.class);
+ Activate a2 = o2.getClass().getAnnotation(Activate.class);
+ if ((a1.before().length > 0 || a1.after().length > 0
+ || a2.before().length > 0 || a2.after().length > 0)
+ && o1.getClass().getInterfaces().length > 0
+ && o1.getClass().getInterfaces()[0].isAnnotationPresent(SPI.class)) {
+ ExtensionLoader<?> extensionLoader = ExtensionLoader.getExtensionLoader(o1.getClass().getInterfaces()[0]);
+ if (a1.before().length > 0 || a1.after().length > 0) {
+ String n2 = extensionLoader.getExtensionName(o2.getClass());
+ for (String before : a1.before()) {
+ if (before.equals(n2)) {
+ return -1;
+ }
+ }
+ for (String after : a1.after()) {
+ if (after.equals(n2)) {
+ return 1;
+ }
+ }
+ }
+ if (a2.before().length > 0 || a2.after().length > 0) {
+ String n1 = extensionLoader.getExtensionName(o1.getClass());
+ for (String before : a2.before()) {
+ if (before.equals(n1)) {
+ return 1;
+ }
+ }
+ for (String after : a2.after()) {
+ if (after.equals(n1)) {
+ return -1;
+ }
+ }
+ }
+ }
+ int n1 = a1 == null ? 0 : a1.order();
+ int n2 = a2 == null ? 0 : a2.order();
+ return n1 > n2 ? 1 : -1; // 就算n1 == n2也不能返回0,否则在HashSet等集合中,会被认为是同一值而覆盖
+ }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/Bytes.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/Bytes.java
new file mode 100644
index 0000000..c774da9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/Bytes.java
@@ -0,0 +1,970 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+import com.alibaba.dubbo.common.utils.IOUtils;
+
+/**
+ * CodecUtils.
+ *
+ * @author qian.lei
+ */
+
+public class Bytes
+{
+ private static final String C64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; //default base64.
+
+ private static final char[] BASE16 = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}, BASE64 = C64.toCharArray();
+
+ private static final int MASK4 = 0x0f, MASK6 = 0x3f, MASK8 = 0xff;
+
+ private static final Map<Integer, byte[]> DECODE_TABLE_MAP = new ConcurrentHashMap<Integer, byte[]>();
+
+ private static ThreadLocal<MessageDigest> MD = new ThreadLocal<MessageDigest>();
+
+ /**
+ * byte array copy.
+ *
+ * @param src src.
+ * @param length new length.
+ * @return new byte array.
+ */
+ public static byte[] copyOf(byte[] src, int length)
+ {
+ byte[] dest = new byte[length];
+ System.arraycopy(src, 0, dest, 0, Math.min(src.length, length));
+ return dest;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] short2bytes(short v)
+ {
+ byte[] ret = { 0, 0 };
+ short2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void short2bytes(short v, byte[] b)
+ {
+ short2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void short2bytes(short v, byte[] b, int off)
+ {
+ b[off + 1] = (byte) v;
+ b[off + 0] = (byte) (v >>> 8);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] int2bytes(int v)
+ {
+ byte[] ret = { 0, 0, 0, 0 };
+ int2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void int2bytes(int v, byte[] b)
+ {
+ int2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ * @param off array offset.
+ */
+ public static void int2bytes(int v, byte[] b, int off)
+ {
+ b[off + 3] = (byte) v;
+ b[off + 2] = (byte) (v >>> 8);
+ b[off + 1] = (byte) (v >>> 16);
+ b[off + 0] = (byte) (v >>> 24);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] float2bytes(float v)
+ {
+ byte[] ret = { 0, 0, 0, 0 };
+ float2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void float2bytes(float v, byte[] b)
+ {
+ float2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ * @param off array offset.
+ */
+ public static void float2bytes(float v, byte[] b, int off)
+ {
+ int i = Float.floatToIntBits(v);
+ b[off + 3] = (byte) i;
+ b[off + 2] = (byte) (i >>> 8);
+ b[off + 1] = (byte) (i >>> 16);
+ b[off + 0] = (byte) (i >>> 24);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] long2bytes(long v)
+ {
+ byte[] ret = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ long2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void long2bytes(long v, byte[] b)
+ {
+ long2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ * @param off array offset.
+ */
+ public static void long2bytes(long v, byte[] b, int off)
+ {
+ b[off + 7] = (byte) v;
+ b[off + 6] = (byte) (v >>> 8);
+ b[off + 5] = (byte) (v >>> 16);
+ b[off + 4] = (byte) (v >>> 24);
+ b[off + 3] = (byte) (v >>> 32);
+ b[off + 2] = (byte) (v >>> 40);
+ b[off + 1] = (byte) (v >>> 48);
+ b[off + 0] = (byte) (v >>> 56);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] double2bytes(double v)
+ {
+ byte[] ret = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ double2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void double2bytes(double v, byte[] b)
+ {
+ double2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ * @param off array offset.
+ */
+ public static void double2bytes(double v, byte[] b, int off)
+ {
+ long j = Double.doubleToLongBits(v);
+ b[off + 7] = (byte) j;
+ b[off + 6] = (byte) (j >>> 8);
+ b[off + 5] = (byte) (j >>> 16);
+ b[off + 4] = (byte) (j >>> 24);
+ b[off + 3] = (byte) (j >>> 32);
+ b[off + 2] = (byte) (j >>> 40);
+ b[off + 1] = (byte) (j >>> 48);
+ b[off + 0] = (byte) (j >>> 56);
+ }
+
+ /**
+ * to short.
+ *
+ * @param b byte array.
+ * @return short.
+ */
+ public static short bytes2short(byte[] b)
+ {
+ return bytes2short(b, 0);
+ }
+
+ /**
+ * to short.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return short.
+ */
+ public static short bytes2short(byte[] b, int off)
+ {
+ return (short) (((b[off + 1] & 0xFF) << 0) +
+ ((b[off + 0]) << 8));
+ }
+
+ /**
+ * to int.
+ *
+ * @param b byte array.
+ * @return int.
+ */
+ public static int bytes2int(byte[] b)
+ {
+ return bytes2int(b, 0);
+ }
+
+ /**
+ * to int.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return int.
+ */
+ public static int bytes2int(byte[] b, int off)
+ {
+ return ((b[off + 3] & 0xFF) << 0) +
+ ((b[off + 2] & 0xFF) << 8) +
+ ((b[off + 1] & 0xFF) << 16) +
+ ((b[off + 0]) << 24);
+ }
+
+ /**
+ * to int.
+ *
+ * @param b byte array.
+ * @return int.
+ */
+ public static float bytes2float(byte[] b)
+ {
+ return bytes2float(b, 0);
+ }
+
+ /**
+ * to int.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return int.
+ */
+ public static float bytes2float(byte[] b, int off)
+ {
+ int i = ((b[off + 3] & 0xFF) << 0) +
+ ((b[off + 2] & 0xFF) << 8) +
+ ((b[off + 1] & 0xFF) << 16) +
+ ((b[off + 0]) << 24);
+ return Float.intBitsToFloat(i);
+ }
+
+ /**
+ * to long.
+ *
+ * @param b byte array.
+ * @return long.
+ */
+ public static long bytes2long(byte[] b)
+ {
+ return bytes2long(b,0);
+ }
+
+ /**
+ * to long.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return long.
+ */
+ public static long bytes2long(byte[] b,int off)
+ {
+ return ((b[off + 7] & 0xFFL) << 0) +
+ ((b[off + 6] & 0xFFL) << 8) +
+ ((b[off + 5] & 0xFFL) << 16) +
+ ((b[off + 4] & 0xFFL) << 24) +
+ ((b[off + 3] & 0xFFL) << 32) +
+ ((b[off + 2] & 0xFFL) << 40) +
+ ((b[off + 1] & 0xFFL) << 48) +
+ (((long) b[off + 0]) << 56);
+ }
+
+ /**
+ * to long.
+ *
+ * @param b byte array.
+ * @return double.
+ */
+ public static double bytes2double(byte[] b)
+ {
+ return bytes2double(b,0);
+ }
+
+ /**
+ * to long.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return double.
+ */
+ public static double bytes2double(byte[] b, int off)
+ {
+ long j = ((b[off + 7] & 0xFFL) << 0) +
+ ((b[off + 6] & 0xFFL) << 8) +
+ ((b[off + 5] & 0xFFL) << 16) +
+ ((b[off + 4] & 0xFFL) << 24) +
+ ((b[off + 3] & 0xFFL) << 32) +
+ ((b[off + 2] & 0xFFL) << 40) +
+ ((b[off + 1] & 0xFFL) << 48) +
+ (((long) b[off + 0]) << 56);
+ return Double.longBitsToDouble(j);
+ }
+
+ /**
+ * to hex string.
+ *
+ * @param bs byte array.
+ * @return hex string.
+ */
+ public static String bytes2hex(byte[] bs)
+ {
+ return bytes2hex(bs, 0, bs.length);
+ }
+
+ /**
+ * to hex string.
+ *
+ * @param bs byte array.
+ * @param off offset.
+ * @param len length.
+ * @return hex string.
+ */
+ public static String bytes2hex(byte[] bs, int off, int len)
+ {
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("bytes2hex: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("bytes2hex: length < 0, length is " + len );
+ if( off + len > bs.length )
+ throw new IndexOutOfBoundsException("bytes2hex: offset + length > array length.");
+
+ byte b;
+ int r = off, w = 0;
+ char[] cs = new char[len*2];
+ for(int i=0;i<len;i++)
+ {
+ b = bs[r++];
+ cs[w++] = BASE16[ b >> 4 & MASK4 ];
+ cs[w++] = BASE16[ b & MASK4 ];
+ }
+ return new String(cs);
+ }
+
+ /**
+ * from hex string.
+ *
+ * @param str hex string.
+ * @return byte array.
+ */
+ public static byte[] hex2bytes(String str)
+ {
+ return hex2bytes(str, 0, str.length());
+ }
+
+ /**
+ * from hex string.
+ *
+ * @param str hex string.
+ * @param off offset.
+ * @param len length.
+ * @return byte array.
+ */
+ public static byte[] hex2bytes(final String str, final int off, int len)
+ {
+ if( ( len & 1 ) == 1 )
+ throw new IllegalArgumentException("hex2bytes: ( len & 1 ) == 1.");
+
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("hex2bytes: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("hex2bytes: length < 0, length is " + len );
+ if( off + len > str.length() )
+ throw new IndexOutOfBoundsException("hex2bytes: offset + length > array length.");
+
+ int num = len / 2, r = off, w = 0;
+ byte[] b = new byte[num];
+ for(int i=0;i<num;i++)
+ b[w++] = (byte)( hex(str.charAt(r++)) << 4 | hex(str.charAt(r++)) );
+ return b;
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b)
+ {
+ return bytes2base64(b, 0, b.length, BASE64);
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b, int offset, int length)
+ {
+ return bytes2base64(b, offset, length, BASE64);
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @param code base64 code string(0-63 is base64 char,64 is pad char).
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b, String code)
+ {
+ return bytes2base64(b, 0, b.length, code);
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @param code base64 code string(0-63 is base64 char,64 is pad char).
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b, int offset, int length, String code)
+ {
+ if( code.length() < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+
+ return bytes2base64(b, offset, length, code.toCharArray());
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b, char[] code)
+ {
+ return bytes2base64(b, 0, b.length, code);
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param bs byte array.
+ * @param off offset.
+ * @param len length.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return base64 string.
+ */
+ public static String bytes2base64(final byte[] bs, final int off, final int len, final char[] code)
+ {
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("bytes2base64: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("bytes2base64: length < 0, length is " + len );
+ if( off + len > bs.length )
+ throw new IndexOutOfBoundsException("bytes2base64: offset + length > array length.");
+
+ if( code.length < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+
+ boolean pad = code.length > 64; // has pad char.
+ int num = len / 3, rem = len % 3, r = off, w = 0;
+ char[] cs = new char[ num * 4 + ( rem == 0 ? 0 : pad ? 4 : rem + 1 ) ];
+
+ for(int i=0;i<num;i++)
+ {
+ int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8, b3 = bs[r++] & MASK8;
+
+ cs[w++] = code[ b1 >> 2 ];
+ cs[w++] = code[ ( b1 << 4 ) & MASK6 | ( b2 >> 4 ) ];
+ cs[w++] = code[ ( b2 << 2 ) & MASK6 | ( b3 >> 6 ) ];
+ cs[w++] = code[ b3 & MASK6 ];
+ }
+
+ if( rem == 1 )
+ {
+ int b1 = bs[r++] & MASK8;
+ cs[w++] = code[ b1 >> 2 ];
+ cs[w++] = code[ ( b1 << 4 ) & MASK6 ];
+ if( pad )
+ {
+ cs[w++] = code[64];
+ cs[w++] = code[64];
+ }
+ }
+ else if( rem == 2 )
+ {
+ int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8;
+ cs[w++] = code[ b1 >> 2 ];
+ cs[w++] = code[ ( b1 << 4 ) & MASK6 | ( b2 >> 4 ) ];
+ cs[w++] = code[ ( b2 << 2 ) & MASK6 ];
+ if( pad )
+ cs[w++] = code[64];
+ }
+ return new String(cs);
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @return byte array.
+ */
+ public static byte[] base642bytes(String str)
+ {
+ return base642bytes(str, 0, str.length());
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param offset offset.
+ * @param length length.
+ * @return byte array.
+ */
+ public static byte[] base642bytes(String str, int offset, int length)
+ {
+ return base642bytes(str, offset, length, C64);
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return byte array.
+ */
+ public static byte[] base642bytes(String str, String code)
+ {
+ return base642bytes(str, 0, str.length(), code);
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param off offset.
+ * @param len length.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return byte array.
+ */
+ public static byte[] base642bytes(final String str, final int off, final int len, final String code)
+ {
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len );
+ if( off + len > str.length() )
+ throw new IndexOutOfBoundsException("base642bytes: offset + length > string length.");
+
+ if( code.length() < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+
+ int rem = len % 4;
+ if( rem == 1 )
+ throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1.");
+
+ int num = len / 4, size = num * 3;
+ if( code.length() > 64 )
+ {
+ if( rem != 0 )
+ throw new IllegalArgumentException("base642bytes: base64 string length error.");
+
+ char pc = code.charAt(64);
+ if( str.charAt(off+len-2) == pc )
+ {
+ size -= 2;
+ --num;
+ rem = 2;
+ }
+ else if( str.charAt(off+len-1) == pc )
+ {
+ size--;
+ --num;
+ rem = 3;
+ }
+ }
+ else
+ {
+ if( rem == 2 )
+ size++;
+ else if( rem == 3 )
+ size += 2;
+ }
+
+ int r = off, w = 0;
+ byte[] b = new byte[size], t = decodeTable(code);
+ for(int i=0;i<num;i++)
+ {
+ int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
+ int c3 = t[str.charAt(r++)], c4 = t[str.charAt(r++)];
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+ b[w++] = (byte)( ( c3 << 6 ) | c4 );
+ }
+
+ if( rem == 2 )
+ {
+ int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ }
+ else if( rem == 3 )
+ {
+ int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)], c3 = t[str.charAt(r++)];
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+ }
+ return b;
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return byte array.
+ */
+ public static byte[] base642bytes(String str, char[] code)
+ {
+ return base642bytes(str, 0, str.length(), code);
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param off offset.
+ * @param len length.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return byte array.
+ */
+ public static byte[] base642bytes(final String str, final int off, final int len, final char[] code)
+ {
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len );
+ if( off + len > str.length() )
+ throw new IndexOutOfBoundsException("base642bytes: offset + length > string length.");
+
+ if( code.length < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+
+ int rem = len % 4;
+ if( rem == 1 )
+ throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1.");
+
+ int num = len / 4, size = num * 3;
+ if( code.length > 64 )
+ {
+ if( rem != 0 )
+ throw new IllegalArgumentException("base642bytes: base64 string length error.");
+
+ char pc = code[64];
+ if( str.charAt(off+len-2) == pc )
+ size -= 2;
+ else if( str.charAt(off+len-1) == pc )
+ size--;
+ }
+ else
+ {
+ if( rem == 2 )
+ size++;
+ else if( rem == 3 )
+ size += 2;
+ }
+
+ int r = off, w = 0;
+ byte[] b = new byte[size];
+ for(int i=0;i<num;i++)
+ {
+ int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
+ int c3 = indexOf(code, str.charAt(r++)), c4 = indexOf(code, str.charAt(r++));
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+ b[w++] = (byte)( ( c3 << 6 ) | c4 );
+ }
+
+ if( rem == 2 )
+ {
+ int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ }
+ else if( rem == 3 )
+ {
+ int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)), c3 = indexOf(code, str.charAt(r++));
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+ }
+ return b;
+ }
+
+ /**
+ * zip.
+ *
+ * @param bytes source.
+ * @return compressed byte array.
+ * @throws IOException.
+ */
+ public static byte[] zip(byte[] bytes) throws IOException
+ {
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
+ OutputStream os = new DeflaterOutputStream(bos);
+ try
+ {
+ os.write(bytes);
+ }
+ finally
+ {
+ os.close();
+ bos.close();
+ }
+ return bos.toByteArray();
+ }
+
+ /**
+ * unzip.
+ *
+ * @param bytes compressed byte array.
+ * @return byte uncompressed array.
+ * @throws IOException
+ */
+ public static byte[] unzip(byte[] bytes) throws IOException
+ {
+ UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(bytes);
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
+ InputStream is = new InflaterInputStream(bis);
+ try
+ {
+ IOUtils.write(is, bos);
+ return bos.toByteArray();
+ }
+ finally
+ {
+ is.close();
+ bis.close();
+ bos.close();
+ }
+ }
+
+ /**
+ * get md5.
+ *
+ * @param str input string.
+ * @return MD5 byte array.
+ */
+ public static byte[] getMD5(String str)
+ {
+ return getMD5(str.getBytes());
+ }
+
+ /**
+ * get md5.
+ *
+ * @param source byte array source.
+ * @return MD5 byte array.
+ */
+ public static byte[] getMD5(byte[] source)
+ {
+ MessageDigest md = getMessageDigest();
+ return md.digest(source);
+ }
+
+ /**
+ * get md5.
+ *
+ * @param file file source.
+ * @return MD5 byte array.
+ */
+ public static byte[] getMD5(File file) throws IOException
+ {
+ InputStream is = new FileInputStream(file);
+ try{ return getMD5(is); }
+ finally{ is.close(); }
+ }
+
+ /**
+ * get md5.
+ *
+ * @param is input stream.
+ * @return MD5 byte array.
+ */
+ public static byte[] getMD5(InputStream is) throws IOException
+ {
+ return getMD5(is, 1024 * 8);
+ }
+
+ private static byte hex(char c)
+ {
+ if( c <= '9' ) return (byte)( c - '0' );
+ if( c >= 'a' && c <= 'f' ) return (byte)( c - 'a' + 10 );
+ if( c >= 'A' && c <= 'F' ) return (byte)( c - 'A' + 10 );
+ throw new IllegalArgumentException("hex string format error [" + c + "].");
+ }
+
+ private static int indexOf(char[] cs, char c)
+ {
+ for(int i=0,len=cs.length;i<len;i++)
+ if( cs[i] == c ) return i;
+ return -1;
+ }
+
+ private static byte[] decodeTable(String code)
+ {
+ int hash = code.hashCode();
+ byte[] ret = DECODE_TABLE_MAP.get(hash);
+ if( ret == null )
+ {
+ if( code.length() < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+ // create new decode table.
+ ret = new byte[128];
+ for(int i=0;i<128;i++) // init table.
+ ret[i] = -1;
+ for(int i=0;i<64;i++)
+ ret[code.charAt(i)] = (byte)i;
+ DECODE_TABLE_MAP.put(hash, ret);
+ }
+ return ret;
+ }
+
+ private static byte[] getMD5(InputStream is, int bs) throws IOException
+ {
+ MessageDigest md = getMessageDigest();
+ byte[] buf = new byte[bs];
+ while( is.available() > 0 )
+ {
+ int read, total = 0;
+ do
+ {
+ if( ( read = is.read(buf, total, bs-total) ) <= 0 )
+ break;
+ total += read;
+ }
+ while( total < bs );
+ md.update(buf);
+ }
+ return md.digest();
+ }
+
+ private static MessageDigest getMessageDigest()
+ {
+ MessageDigest ret = MD.get();
+ if( ret == null )
+ {
+ try
+ {
+ ret = MessageDigest.getInstance("MD5");
+ MD.set(ret);
+ }
+ catch(NoSuchAlgorithmException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ return ret;
+ }
+
+ private Bytes(){}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/ClassDescriptorMapper.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/ClassDescriptorMapper.java
new file mode 100644
index 0000000..96076bb
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/ClassDescriptorMapper.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+public interface ClassDescriptorMapper
+{
+ /**
+ * get Class-Descriptor by index.
+ *
+ * @param index index.
+ * @return string.
+ */
+ String getDescriptor(int index);
+
+ /**
+ * get Class-Descriptor index
+ *
+ * @param desc Class-Descriptor
+ * @return index.
+ */
+ int getDescriptorIndex(String desc);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/StreamUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/StreamUtils.java
new file mode 100644
index 0000000..d349655
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/StreamUtils.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Stream utils.
+ *
+ * @author qian.lei
+ * @author ding.lid
+ */
+
+public class StreamUtils
+{
+ private StreamUtils(){}
+
+ public static InputStream limitedInputStream(final InputStream is, final int limit) throws IOException
+ {
+ return new InputStream(){
+ private int mPosition = 0, mMark = 0, mLimit = Math.min(limit, is.available());
+
+ public int read() throws IOException
+ {
+ if( mPosition < mLimit )
+ {
+ mPosition++;
+ return is.read();
+ }
+ return -1;
+ }
+
+ public int read(byte b[], int off, int len) throws IOException
+ {
+ if( b == null )
+ throw new NullPointerException();
+
+ if( off < 0 || len < 0 || len > b.length - off )
+ throw new IndexOutOfBoundsException();
+
+ if( mPosition >= mLimit )
+ return -1;
+
+ if( mPosition + len > mLimit )
+ len = mLimit - mPosition;
+
+ if( len <= 0 )
+ return 0;
+
+ is.read(b, off, len);
+ mPosition += len;
+ return len;
+ }
+
+ public long skip(long len) throws IOException
+ {
+ if( mPosition + len > mLimit )
+ len = mLimit - mPosition;
+
+ if( len <= 0 )
+ return 0;
+
+ is.skip(len);
+ mPosition += len;
+ return len;
+ }
+
+ public int available()
+ {
+ return mLimit - mPosition;
+ }
+
+ public boolean markSupported()
+ {
+ return is.markSupported();
+ }
+
+ public void mark(int readlimit)
+ {
+ is.mark(readlimit);
+ mMark = mPosition;
+ }
+
+ public void reset() throws IOException
+ {
+ is.reset();
+ mPosition = mMark;
+ }
+
+ public void close() throws IOException
+ {}
+ };
+ }
+
+ public static InputStream markSupportedInputStream(final InputStream is, final int markBufferSize) {
+ if(is.markSupported()) {
+ return is;
+ }
+
+ return new InputStream() {
+ byte[] mMarkBuffer;
+
+ boolean mInMarked = false;
+ boolean mInReset = false;
+ private int mPosition = 0;
+ private int mCount = 0;
+
+ boolean mDry = false;
+
+ @Override
+ public int read() throws IOException {
+ if(!mInMarked) {
+ return is.read();
+ }
+ else {
+ if(mPosition < mCount) {
+ byte b = mMarkBuffer[mPosition++];
+ return b & 0xFF;
+ }
+
+ if(!mInReset) {
+ if(mDry) return -1;
+
+ if(null == mMarkBuffer) {
+ mMarkBuffer = new byte[markBufferSize];
+ }
+ if(mPosition >= markBufferSize) {
+ throw new IOException("Mark buffer is full!");
+ }
+
+ int read = is.read();
+ if(-1 == read){
+ mDry = true;
+ return -1;
+ }
+
+ mMarkBuffer[mPosition++] = (byte) read;
+ mCount++;
+
+ return read;
+ }
+ else {
+ // mark buffer is used, exit mark status!
+ mInMarked = false;
+ mInReset = false;
+ mPosition = 0;
+ mCount = 0;
+
+ return is.read();
+ }
+ }
+ }
+
+ /**
+ * NOTE: the <code>readlimit</code> argument for this class
+ * has no meaning.
+ */
+ @Override
+ public synchronized void mark(int readlimit) {
+ mInMarked = true;
+ mInReset = false;
+
+ // mark buffer is not empty
+ int count = mCount - mPosition;
+ if(count > 0) {
+ System.arraycopy(mMarkBuffer, mPosition, mMarkBuffer, 0, count);
+ mCount = count;
+ mPosition = 0;
+ }
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ if(!mInMarked) {
+ throw new IOException("should mark befor reset!");
+ }
+
+ mInReset = true;
+ mPosition = 0;
+ }
+
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public int available() throws IOException {
+ int available = is.available();
+
+ if(mInMarked && mInReset) available += mCount - mPosition;
+
+ return available;
+ }
+ };
+ }
+
+ public static InputStream markSupportedInputStream(final InputStream is) {
+ return markSupportedInputStream(is, 1024);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayInputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayInputStream.java
new file mode 100644
index 0000000..248b466
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayInputStream.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * UnsafeByteArrayInputStrem.
+ *
+ * @author qian.lei
+ */
+
+public class UnsafeByteArrayInputStream extends InputStream
+{
+ protected byte mData[];
+
+ protected int mPosition, mLimit, mMark = 0;
+
+ public UnsafeByteArrayInputStream(byte buf[])
+ {
+ this(buf, 0, buf.length);
+ }
+
+ public UnsafeByteArrayInputStream(byte buf[], int offset)
+ {
+ this(buf, offset, buf.length-offset);
+ }
+
+ public UnsafeByteArrayInputStream(byte buf[], int offset, int length)
+ {
+ mData = buf;
+ mPosition = mMark = offset;
+ mLimit = Math.min(offset+length, buf.length);
+ }
+
+ public int read()
+ {
+ return ( mPosition < mLimit ) ? ( mData[mPosition++] & 0xff ) : -1;
+ }
+
+ public int read(byte b[], int off, int len)
+ {
+ if( b == null )
+ throw new NullPointerException();
+ if( off < 0 || len < 0 || len > b.length - off )
+ throw new IndexOutOfBoundsException();
+ if( mPosition >= mLimit )
+ return -1;
+ if( mPosition + len > mLimit )
+ len = mLimit - mPosition;
+ if( len <= 0 )
+ return 0;
+ System.arraycopy(mData, mPosition, b, off, len);
+ mPosition += len;
+ return len;
+ }
+
+ public long skip(long len)
+ {
+ if( mPosition + len > mLimit )
+ len = mLimit - mPosition;
+ if( len <= 0 )
+ return 0;
+ mPosition += len;
+ return len;
+ }
+
+ public int available()
+ {
+ return mLimit - mPosition;
+ }
+
+ public boolean markSupported()
+ {
+ return true;
+ }
+
+ public void mark(int readAheadLimit)
+ {
+ mMark = mPosition;
+ }
+
+ public void reset()
+ {
+ mPosition = mMark;
+ }
+
+ public void close() throws IOException
+ {}
+
+ public int position()
+ {
+ return mPosition;
+ }
+
+ public void position(int newPosition)
+ {
+ mPosition = newPosition;
+ }
+
+ public int size() {
+ return mData == null ? 0 : mData.length;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayOutputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayOutputStream.java
new file mode 100644
index 0000000..3243dcd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeByteArrayOutputStream.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+
+/**
+ * UnsafeByteArrayOutputStream.
+ *
+ * @author qian.lei
+ */
+
+public class UnsafeByteArrayOutputStream extends OutputStream
+{
+ protected byte mBuffer[];
+
+ protected int mCount;
+
+ public UnsafeByteArrayOutputStream()
+ {
+ this(32);
+ }
+
+ public UnsafeByteArrayOutputStream(int size)
+ {
+ if( size < 0 )
+ throw new IllegalArgumentException("Negative initial size: " + size);
+ mBuffer = new byte[size];
+ }
+
+ public void write(int b)
+ {
+ int newcount = mCount + 1;
+ if( newcount > mBuffer.length )
+ mBuffer = Bytes.copyOf(mBuffer, Math.max(mBuffer.length << 1, newcount));
+ mBuffer[mCount] = (byte)b;
+ mCount = newcount;
+ }
+
+ public void write(byte b[], int off, int len)
+ {
+ if( ( off < 0 ) || ( off > b.length ) || ( len < 0 ) || ( ( off + len ) > b.length ) || ( ( off + len ) < 0 ) )
+ throw new IndexOutOfBoundsException();
+ if( len == 0 )
+ return;
+ int newcount = mCount + len;
+ if( newcount > mBuffer.length )
+ mBuffer = Bytes.copyOf(mBuffer, Math.max(mBuffer.length << 1, newcount));
+ System.arraycopy(b, off, mBuffer, mCount, len);
+ mCount = newcount;
+ }
+
+ public int size()
+ {
+ return mCount;
+ }
+
+ public void reset()
+ {
+ mCount = 0;
+ }
+
+ public byte[] toByteArray()
+ {
+ return Bytes.copyOf(mBuffer, mCount);
+ }
+
+ public ByteBuffer toByteBuffer()
+ {
+ return ByteBuffer.wrap(mBuffer, 0, mCount);
+ }
+
+ public void writeTo(OutputStream out) throws IOException
+ {
+ out.write(mBuffer, 0, mCount);
+ }
+
+ public String toString()
+ {
+ return new String(mBuffer, 0, mCount);
+ }
+
+ public String toString(String charset) throws UnsupportedEncodingException
+ {
+ return new String(mBuffer, 0, mCount, charset);
+ }
+
+ public void close() throws IOException
+ {}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringReader.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringReader.java
new file mode 100644
index 0000000..3db1b5a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringReader.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Thread unsafed StringReader.
+ *
+ * @author qian.lei
+ */
+
+public class UnsafeStringReader extends Reader
+{
+ private String mString;
+
+ private int mPosition, mLimit, mMark;
+
+ public UnsafeStringReader(String str)
+ {
+ mString = str;
+ mLimit = str.length();
+ mPosition = mMark = 0;
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ ensureOpen();
+ if( mPosition >= mLimit )
+ return -1;
+
+ return mString.charAt(mPosition++);
+ }
+
+ @Override
+ public int read(char[] cs, int off, int len) throws IOException
+ {
+ ensureOpen();
+ if( (off < 0) || (off > cs.length) || (len < 0) ||
+ ((off + len) > cs.length) || ((off + len) < 0) )
+ throw new IndexOutOfBoundsException();
+
+ if( len == 0 )
+ return 0;
+
+ if( mPosition >= mLimit )
+ return -1;
+
+ int n = Math.min(mLimit - mPosition, len);
+ mString.getChars(mPosition, mPosition + n, cs, off);
+ mPosition += n;
+ return n;
+ }
+
+ public long skip(long ns) throws IOException
+ {
+ ensureOpen();
+ if( mPosition >= mLimit )
+ return 0;
+
+ long n = Math.min(mLimit - mPosition, ns);
+ n = Math.max(-mPosition, n);
+ mPosition += n;
+ return n;
+ }
+
+ public boolean ready() throws IOException
+ {
+ ensureOpen();
+ return true;
+ }
+
+ @Override
+ public boolean markSupported()
+ {
+ return true;
+ }
+
+ public void mark(int readAheadLimit) throws IOException
+ {
+ if( readAheadLimit < 0 )
+ throw new IllegalArgumentException("Read-ahead limit < 0");
+
+ ensureOpen();
+ mMark = mPosition;
+ }
+
+ public void reset() throws IOException
+ {
+ ensureOpen();
+ mPosition = mMark;
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ mString = null;
+ }
+
+ private void ensureOpen() throws IOException
+ {
+ if( mString == null )
+ throw new IOException("Stream closed");
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringWriter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringWriter.java
new file mode 100644
index 0000000..7761fe1
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/UnsafeStringWriter.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Thread unsafed StringWriter.
+ *
+ * @author qian.lei
+ */
+
+public class UnsafeStringWriter extends Writer
+{
+ private StringBuilder mBuffer;
+
+ public UnsafeStringWriter()
+ {
+ lock = mBuffer = new StringBuilder();
+ }
+
+ public UnsafeStringWriter(int size)
+ {
+ if( size < 0 )
+ throw new IllegalArgumentException("Negative buffer size");
+
+ lock = mBuffer = new StringBuilder();
+ }
+
+ @Override
+ public void write(int c)
+ {
+ mBuffer.append((char)c);
+ }
+
+ @Override
+ public void write(char[] cs) throws IOException
+ {
+ mBuffer.append(cs, 0, cs.length);
+ }
+
+ @Override
+ public void write(char[] cs, int off, int len) throws IOException
+ {
+ if( (off < 0) || (off > cs.length) || (len < 0) ||
+ ((off + len) > cs.length) || ((off + len) < 0) )
+ throw new IndexOutOfBoundsException();
+
+ if( len > 0 )
+ mBuffer.append(cs, off, len);
+ }
+
+ @Override
+ public void write(String str)
+ {
+ mBuffer.append(str);
+ }
+
+ @Override
+ public void write(String str, int off, int len)
+ {
+ mBuffer.append(str.substring(off, off + len));
+ }
+
+ @Override
+ public Writer append(CharSequence csq)
+ {
+ if (csq == null)
+ write("null");
+ else
+ write(csq.toString());
+ return this;
+ }
+
+ @Override
+ public Writer append(CharSequence csq, int start, int end)
+ {
+ CharSequence cs = (csq == null ? "null" : csq);
+ write(cs.subSequence(start, end).toString());
+ return this;
+ }
+
+ @Override
+ public Writer append(char c)
+ {
+ mBuffer.append(c);
+ return this;
+ }
+
+ @Override
+ public void close(){}
+
+ @Override
+ public void flush(){}
+
+ @Override
+ public String toString()
+ {
+ return mBuffer.toString();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/GenericJSONConverter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/GenericJSONConverter.java
new file mode 100644
index 0000000..32149c9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/GenericJSONConverter.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.io.Bytes;
+
+public class GenericJSONConverter implements JSONConverter
+{
+ private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+ protected interface Encoder{ void encode(Object obj, JSONWriter jb) throws IOException; }
+
+ protected interface Decoder{ Object decode(Object jv) throws IOException; }
+
+ private static final Map<Class<?>, Encoder> GlobalEncoderMap = new HashMap<Class<?>, Encoder>();
+
+ private static final Map<Class<?>, Decoder> GlobalDecoderMap = new HashMap<Class<?>, Decoder>();
+
+ @SuppressWarnings("unchecked")
+ public void writeValue(Object obj, JSONWriter jb, boolean writeClass) throws IOException
+ {
+ if (obj == null) {
+ jb.valueNull();
+ return;
+ }
+ Class<?> c = obj.getClass();
+ Encoder encoder = GlobalEncoderMap.get(c);
+
+ if( encoder != null )
+ {
+ encoder.encode(obj, jb);
+ }
+ else if( obj instanceof JSONNode )
+ {
+ ((JSONNode)obj).writeJSON(this, jb, writeClass);
+ }
+ else if( c.isEnum() )
+ {
+ jb.valueString(((Enum<?>)obj).name());
+ }
+ else if( c.isArray() )
+ {
+ int len = Array.getLength(obj);
+ jb.arrayBegin();
+ for(int i=0;i<len;i++)
+ writeValue(Array.get(obj, i), jb, writeClass);
+ jb.arrayEnd();
+ }
+ else if( Map.class.isAssignableFrom(c) )
+ {
+ Object key, value;
+ jb.objectBegin();
+ for( Map.Entry<Object, Object> entry : ((Map<Object, Object>)obj).entrySet() )
+ {
+ key = entry.getKey();
+ if( key == null )
+ continue;
+ jb.objectItem(key.toString());
+
+ value = entry.getValue();
+ if( value == null )
+ jb.valueNull();
+ else
+ writeValue(value, jb, writeClass);
+ }
+ jb.objectEnd();
+ }
+ else if( Collection.class.isAssignableFrom(c) )
+ {
+ jb.arrayBegin();
+ for( Object item : (Collection<Object>)obj )
+ {
+ if( item == null )
+ jb.valueNull();
+ else
+ writeValue(item, jb, writeClass);
+ }
+ jb.arrayEnd();
+ }
+ else
+ {
+ jb.objectBegin();
+
+ Wrapper w = Wrapper.getWrapper(c);
+ String pns[] = w.getPropertyNames();
+
+ for( String pn : pns )
+ {
+ if ((obj instanceof Throwable) && (
+ "localizedMessage".equals(pn)
+ || "cause".equals(pn)
+ || "stackTrace".equals(pn))) {
+ continue;
+ }
+
+ jb.objectItem(pn);
+
+ Object value = w.getPropertyValue(obj,pn);
+ if( value == null || value == obj)
+ jb.valueNull();
+ else
+ writeValue(value, jb, writeClass);
+ }
+ if (writeClass) {
+ jb.objectItem(JSONVisitor.CLASS_PROPERTY);
+ writeValue(obj.getClass().getName(), jb, writeClass);
+ }
+ jb.objectEnd();
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Object readValue(Class<?> c, Object jv) throws IOException
+ {
+ if (jv == null) {
+ return null;
+ }
+ Decoder decoder = GlobalDecoderMap.get(c);
+ if( decoder != null ) {
+ return decoder.decode(jv);
+ }
+ if (c.isEnum()) {
+ return Enum.valueOf((Class<Enum>)c, String.valueOf(jv));
+ }
+ return jv;
+ }
+
+ static
+ {
+ // init encoder map.
+ Encoder e = new Encoder(){
+ public void encode(Object obj, JSONWriter jb) throws IOException
+ {
+ jb.valueBoolean((Boolean)obj);
+ }
+ };
+ GlobalEncoderMap.put(boolean.class, e);
+ GlobalEncoderMap.put(Boolean.class, e);
+
+ e = new Encoder(){
+ public void encode(Object obj, JSONWriter jb) throws IOException
+ {
+ jb.valueInt(((Number)obj).intValue());
+ }
+ };
+ GlobalEncoderMap.put(int.class, e);
+ GlobalEncoderMap.put(Integer.class, e);
+ GlobalEncoderMap.put(short.class, e);
+ GlobalEncoderMap.put(Short.class, e);
+ GlobalEncoderMap.put(byte.class, e);
+ GlobalEncoderMap.put(Byte.class, e);
+ GlobalEncoderMap.put(AtomicInteger.class, e);
+
+ e = new Encoder(){
+ public void encode(Object obj, JSONWriter jb) throws IOException
+ {
+ jb.valueString(Character.toString((Character)obj));
+ }
+ };
+ GlobalEncoderMap.put(char.class, e);
+ GlobalEncoderMap.put(Character.class, e);
+
+ e = new Encoder(){
+ public void encode(Object obj, JSONWriter jb) throws IOException
+ {
+ jb.valueLong(((Number)obj).longValue());
+ }
+ };
+ GlobalEncoderMap.put(long.class, e);
+ GlobalEncoderMap.put(Long.class, e);
+ GlobalEncoderMap.put(AtomicLong.class, e);
+ GlobalEncoderMap.put(BigInteger.class, e);
+
+ e = new Encoder(){
+ public void encode(Object obj, JSONWriter jb) throws IOException
+ {
+ jb.valueFloat(((Number)obj).floatValue());
+ }
+ };
+ GlobalEncoderMap.put(float.class, e);
+ GlobalEncoderMap.put(Float.class, e);
+
+ e = new Encoder(){
+ public void encode(Object obj, JSONWriter jb) throws IOException
+ {
+ jb.valueDouble(((Number)obj).doubleValue());
+ }
+ };
+ GlobalEncoderMap.put(double.class, e);
+ GlobalEncoderMap.put(Double.class, e);
+ GlobalEncoderMap.put(BigDecimal.class, e);
+
+ e = new Encoder(){
+ public void encode(Object obj, JSONWriter jb) throws IOException
+ {
+ jb.valueString(obj.toString());
+ }
+ };
+ GlobalEncoderMap.put(String.class, e);
+ GlobalEncoderMap.put(StringBuilder.class, e);
+ GlobalEncoderMap.put(StringBuffer.class, e);
+
+ e = new Encoder(){
+ public void encode(Object obj, JSONWriter jb) throws IOException
+ {
+ jb.valueString(Bytes.bytes2base64((byte[])obj));
+ }
+ };
+ GlobalEncoderMap.put(byte[].class, e);
+
+ e = new Encoder(){
+ public void encode(Object obj, JSONWriter jb) throws IOException
+ {
+ jb.valueString(new SimpleDateFormat(DATE_FORMAT).format((Date)obj));
+ }
+ };
+ GlobalEncoderMap.put(Date.class, e);
+
+ // init decoder map.
+ Decoder d = new Decoder(){
+ public Object decode(Object jv){
+ return jv.toString();
+ }
+ };
+ GlobalDecoderMap.put(String.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Boolean ) return ((Boolean)jv).booleanValue();
+ return false;
+ }
+ };
+ GlobalDecoderMap.put(boolean.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Boolean ) return (Boolean)jv;
+ return (Boolean)null;
+ }
+ };
+ GlobalDecoderMap.put(Boolean.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof String && ((String)jv).length() > 0) return ((String)jv).charAt(0);
+ return (char)0;
+ }
+ };
+ GlobalDecoderMap.put(char.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof String && ((String)jv).length() > 0) return ((String)jv).charAt(0);
+ return (Character)null;
+ }
+ };
+ GlobalDecoderMap.put(Character.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return ((Number)jv).intValue();
+ return 0;
+ }
+ };
+ GlobalDecoderMap.put(int.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return Integer.valueOf(((Number)jv).intValue());
+ return (Integer)null;
+ }
+ };
+ GlobalDecoderMap.put(Integer.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return ((Number)jv).shortValue();
+ return (short)0;
+ }
+ };
+ GlobalDecoderMap.put(short.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return Short.valueOf(((Number)jv).shortValue());
+ return (Short)null;
+ }
+ };
+ GlobalDecoderMap.put(Short.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return ((Number)jv).longValue();
+ return (long)0;
+ }
+ };
+ GlobalDecoderMap.put(long.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return Long.valueOf(((Number)jv).longValue());
+ return (Long)null;
+ }
+ };
+ GlobalDecoderMap.put(Long.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return ((Number)jv).floatValue();
+ return (float)0;
+ }
+ };
+ GlobalDecoderMap.put(float.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return new Float(((Number)jv).floatValue());
+ return (Float)null;
+ }
+ };
+ GlobalDecoderMap.put(Float.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return ((Number)jv).doubleValue();
+ return (double)0;
+ }
+ };
+ GlobalDecoderMap.put(double.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return new Double(((Number)jv).doubleValue());
+ return (Double)null;
+ }
+ };
+ GlobalDecoderMap.put(Double.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return ((Number)jv).byteValue();
+ return (byte)0;
+ }
+ };
+ GlobalDecoderMap.put(byte.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv)
+ {
+ if( jv instanceof Number ) return Byte.valueOf(((Number)jv).byteValue());
+ return (Byte)null;
+ }
+ };
+ GlobalDecoderMap.put(Byte.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv) throws IOException
+ {
+ if( jv instanceof String ) return Bytes.base642bytes((String)jv);
+ return (byte[])null;
+ }
+ };
+ GlobalDecoderMap.put(byte[].class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv) throws IOException{ return new StringBuilder(jv.toString()); }
+ };
+ GlobalDecoderMap.put(StringBuilder.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv) throws IOException{ return new StringBuffer(jv.toString()); }
+ };
+ GlobalDecoderMap.put(StringBuffer.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv) throws IOException
+ {
+ if( jv instanceof Number ) return BigInteger.valueOf(((Number)jv).longValue());
+ return (BigInteger)null;
+ }
+ };
+ GlobalDecoderMap.put(BigInteger.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv) throws IOException
+ {
+ if( jv instanceof Number ) return BigDecimal.valueOf(((Number)jv).doubleValue());
+ return (BigDecimal)null;
+ }
+ };
+ GlobalDecoderMap.put(BigDecimal.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv) throws IOException
+ {
+ if( jv instanceof Number ) return new AtomicInteger(((Number)jv).intValue());
+ return (AtomicInteger)null;
+ }
+ };
+ GlobalDecoderMap.put(AtomicInteger.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv) throws IOException
+ {
+ if( jv instanceof Number ) return new AtomicLong(((Number)jv).longValue());
+ return (AtomicLong)null;
+ }
+ };
+ GlobalDecoderMap.put(AtomicLong.class, d);
+
+ d = new Decoder(){
+ public Object decode(Object jv) throws IOException
+ {
+ if( jv instanceof String ) {
+ try {
+ return new SimpleDateFormat(DATE_FORMAT).parse((String) jv);
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e.getMessage(), e);
+ }
+ }
+ if( jv instanceof Number )
+ return new Date(((Number)jv).longValue());
+ return (Date)null;
+ }
+ };
+ GlobalDecoderMap.put(Date.class, d);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/J2oVisitor.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/J2oVisitor.java
new file mode 100644
index 0000000..132d5ef
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/J2oVisitor.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.utils.Stack;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+/**
+ * JSON to Object visitor.
+ *
+ * @author qian.lei.
+ */
+
+class J2oVisitor implements JSONVisitor
+{
+ public static final boolean[] EMPTY_BOOL_ARRAY = new boolean[0];
+
+ public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+ public static final char[] EMPTY_CHAR_ARRAY = new char[0];
+
+ public static final short[] EMPTY_SHORT_ARRAY = new short[0];
+
+ public static final int[] EMPTY_INT_ARRAY = new int[0];
+
+ public static final long[] EMPTY_LONG_ARRAY = new long[0];
+
+ public static final float[] EMPTY_FLOAT_ARRAY = new float[0];
+
+ public static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
+
+ public static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private Class<?>[] mTypes;
+
+ private Class<?> mType = Object[].class;
+
+ private Object mValue;
+
+ private Wrapper mWrapper;
+
+ private JSONConverter mConverter;
+
+ private Stack<Object> mStack = new Stack<Object>();
+
+ J2oVisitor(Class<?> type, JSONConverter jc)
+ {
+ mType = type;
+ mConverter = jc;
+ }
+
+ J2oVisitor(Class<?>[] types, JSONConverter jc)
+ {
+ mTypes = types;
+ mConverter = jc;
+ }
+
+ public void begin()
+ {}
+
+ public Object end(Object obj, boolean isValue) throws ParseException
+ {
+ mStack.clear();
+ try {
+ return mConverter.readValue(mType, obj);
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ public void objectBegin() throws ParseException
+ {
+ mStack.push(mValue);
+ mStack.push(mType);
+ mStack.push(mWrapper);
+
+ if( mType == Object.class || Map.class.isAssignableFrom(mType) )
+ {
+ if (! mType.isInterface() && mType != Object.class) {
+ try {
+ mValue = mType.newInstance();
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ } else if (mType == ConcurrentMap.class) {
+ mValue = new ConcurrentHashMap<String, Object>();
+ } else {
+ mValue = new HashMap<String, Object>();
+ }
+ mWrapper = null;
+ } else {
+ try {
+ mValue = mType.newInstance();
+ mWrapper = Wrapper.getWrapper(mType);
+ } catch(IllegalAccessException e){
+ throw new ParseException(StringUtils.toString(e));
+ } catch(InstantiationException e){
+ throw new ParseException(StringUtils.toString(e));
+ }
+ }
+ }
+
+ public Object objectEnd(int count)
+ {
+ Object ret = mValue;
+ mWrapper = (Wrapper)mStack.pop();
+ mType = (Class<?>)mStack.pop();
+ mValue = mStack.pop();
+ return ret;
+ }
+
+ public void objectItem(String name)
+ {
+ mStack.push(name); // push name.
+ mType = ( mWrapper == null ? Object.class : mWrapper.getPropertyType(name) );
+ }
+
+ @SuppressWarnings("unchecked")
+ public void objectItemValue(Object obj, boolean isValue) throws ParseException
+ {
+ String name = (String)mStack.pop(); // pop name.
+ if( mWrapper == null )
+ {
+ ((Map<String, Object>)mValue).put(name, obj);
+ }
+ else
+ {
+ if( mType != null )
+ {
+ if( isValue && obj != null )
+ {
+ try
+ {
+ obj = mConverter.readValue(mType, obj);
+ }
+ catch(IOException e)
+ {
+ throw new ParseException(StringUtils.toString(e));
+ }
+ }
+ if (mValue instanceof Throwable && "message".equals(name)) {
+ try {
+ Field field = Throwable.class.getDeclaredField("detailMessage");
+ if (! field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ field.set(mValue, obj);
+ } catch (NoSuchFieldException e) {
+ throw new ParseException(StringUtils.toString(e));
+ } catch (IllegalAccessException e) {
+ throw new ParseException(StringUtils.toString(e));
+ }
+ } else if (! CLASS_PROPERTY.equals(name)) {
+ mWrapper.setPropertyValue(mValue, name, obj);
+ }
+ }
+ }
+ }
+
+ public void arrayBegin() throws ParseException
+ {
+ mStack.push(mType);
+
+ if( mType.isArray() )
+ mType = mType.getComponentType();
+ else if( mType == Object.class || Collection.class.isAssignableFrom(mType) )
+ mType = Object.class;
+ else
+ throw new ParseException("Convert error, can not load json array data into class [" + mType.getName() + "].");
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object arrayEnd(int count) throws ParseException
+ {
+ Object ret;
+ mType = (Class<?>)mStack.get(-1-count);
+
+ if( mType.isArray() )
+ {
+ ret = toArray(mType.getComponentType(), mStack, count);
+ }
+ else
+ {
+ Collection<Object> items;
+ if( mType == Object.class || Collection.class.isAssignableFrom(mType)) {
+ if (! mType.isInterface() && mType != Object.class) {
+ try {
+ items = (Collection<Object>) mType.newInstance();
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ } else if (mType.isAssignableFrom(ArrayList.class)) { // List
+ items = new ArrayList<Object>(count);
+ } else if (mType.isAssignableFrom(HashSet.class)) { // Set
+ items = new HashSet<Object>(count);
+ } else if (mType.isAssignableFrom(LinkedList.class)) { // Queue
+ items = new LinkedList<Object>();
+ } else { // Other
+ items = new ArrayList<Object>(count);
+ }
+ } else {
+ throw new ParseException("Convert error, can not load json array data into class [" + mType.getName() + "].");
+ }
+ for(int i=0;i<count;i++)
+ items.add(mStack.remove(i-count));
+ ret = items;
+ }
+ mStack.pop();
+ return ret;
+ }
+
+ public void arrayItem(int index) throws ParseException
+ {
+ if( mTypes != null && mStack.size() == index+1 )
+ {
+ if( index < mTypes.length )
+ mType = mTypes[index];
+ else
+ throw new ParseException("Can not load json array data into [" + name(mTypes) + "].");
+ }
+ }
+
+ public void arrayItemValue(int index, Object obj, boolean isValue) throws ParseException
+ {
+ if( isValue && obj != null )
+ {
+ try
+ {
+ obj = mConverter.readValue(mType, obj);
+ }
+ catch(IOException e)
+ {
+ throw new ParseException(e.getMessage());
+ }
+ }
+
+ mStack.push(obj);
+ }
+
+ private static Object toArray(Class<?> c, Stack<Object> list, int len) throws ParseException
+ {
+ if( c == String.class )
+ {
+ if( len == 0 )
+ {
+ return EMPTY_STRING_ARRAY;
+ }
+ else
+ {
+ Object o;
+ String ss[] = new String[len];
+ for(int i=len-1;i>=0;i--)
+ {
+ o = list.pop();
+ ss[i] = ( o == null ? null : o.toString() );
+ }
+ return ss;
+ }
+ }
+ if( c == boolean.class )
+ {
+ if( len == 0 ) return EMPTY_BOOL_ARRAY;
+ Object o;
+ boolean[] ret = new boolean[len];
+ for(int i=len-1;i>=0;i--)
+ {
+ o = list.pop();
+ if( o instanceof Boolean )
+ ret[i] = ((Boolean)o).booleanValue();
+ }
+ return ret;
+ }
+ if( c == int.class )
+ {
+ if( len == 0 ) return EMPTY_INT_ARRAY;
+ Object o;
+ int[] ret = new int[len];
+ for(int i=len-1;i>=0;i--)
+ {
+ o = list.pop();
+ if( o instanceof Number )
+ ret[i] = ((Number)o).intValue();
+ }
+ return ret;
+ }
+ if( c == long.class )
+ {
+ if( len == 0 ) return EMPTY_LONG_ARRAY;
+ Object o;
+ long[] ret = new long[len];
+ for(int i=len-1;i>=0;i--)
+ {
+ o = list.pop();
+ if( o instanceof Number )
+ ret[i] = ((Number)o).longValue();
+ }
+ return ret;
+ }
+ if( c == float.class )
+ {
+ if( len == 0 ) return EMPTY_FLOAT_ARRAY;
+ Object o;
+ float[] ret = new float[len];
+ for(int i=len-1;i>=0;i--)
+ {
+ o = list.pop();
+ if( o instanceof Number )
+ ret[i] = ((Number)o).floatValue();
+ }
+ return ret;
+ }
+ if( c == double.class )
+ {
+ if( len == 0 ) return EMPTY_DOUBLE_ARRAY;
+ Object o;
+ double[] ret = new double[len];
+ for(int i=len-1;i>=0;i--)
+ {
+ o = list.pop();
+ if( o instanceof Number )
+ ret[i] = ((Number)o).doubleValue();
+ }
+ return ret;
+ }
+ if( c == byte.class )
+ {
+ if( len == 0 ) return EMPTY_BYTE_ARRAY;
+ Object o;
+ byte[] ret = new byte[len];
+ for(int i=len-1;i>=0;i--)
+ {
+ o = list.pop();
+ if( o instanceof Number )
+ ret[i] = ((Number)o).byteValue();
+ }
+ return ret;
+ }
+ if( c == char.class )
+ {
+ if( len == 0 ) return EMPTY_CHAR_ARRAY;
+ Object o;
+ char[] ret = new char[len];
+ for(int i=len-1;i>=0;i--)
+ {
+ o = list.pop();
+ if( o instanceof Character )
+ ret[i] = ((Character)o).charValue();
+ }
+ return ret;
+ }
+ if( c == short.class )
+ {
+ if( len == 0 ) return EMPTY_SHORT_ARRAY;
+ Object o;
+ short[] ret = new short[len];
+ for(int i=len-1;i>=0;i--)
+ {
+ o = list.pop();
+ if( o instanceof Number )
+ ret[i] = ((Number)o).shortValue();
+ }
+ return ret;
+ }
+
+ Object ret = Array.newInstance(c, len);
+ for(int i=len-1;i>=0;i--)
+ Array.set(ret, i, list.pop());
+ return ret;
+ }
+
+ private static String name(Class<?>[] types)
+ {
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<types.length;i++)
+ {
+ if( i > 0 )
+ sb.append(", ");
+ sb.append(types[i].getName());
+ }
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSON.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSON.java
new file mode 100644
index 0000000..828d9b8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSON.java
@@ -0,0 +1,761 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.utils.Stack;
+
+/**
+ * JSON.
+ *
+ * @author qian.lei
+ */
+
+public class JSON
+{
+ public static final char LBRACE = '{', RBRACE = '}';
+
+ public static final char LSQUARE = '[', RSQUARE = ']';
+
+ public static final char COMMA = ',', COLON = ':', QUOTE = '"';
+
+ public static final String NULL = "null";
+
+ static final JSONConverter DEFAULT_CONVERTER = new GenericJSONConverter();
+
+ // state.
+ public static final byte END = 0, START = 1, OBJECT_ITEM = 2, OBJECT_VALUE = 3, ARRAY_ITEM = 4;
+
+ private static class Entry
+ {
+ byte state;
+ Object value;
+ Entry(byte s, Object v){ state = s; value = v; }
+ }
+
+ private JSON(){}
+
+ /**
+ * json string.
+ *
+ * @param obj object.
+ * @return json string.
+ * @throws IOException.
+ */
+ public static String json(Object obj) throws IOException
+ {
+ if( obj == null ) return NULL;
+ StringWriter sw = new StringWriter();
+ try
+ {
+ json(obj, sw);
+ return sw.getBuffer().toString();
+ }
+ finally{ sw.close(); }
+ }
+
+ /**
+ * write json.
+ *
+ * @param obj object.
+ * @param writer writer.
+ * @throws IOException.
+ */
+ public static void json(Object obj, Writer writer) throws IOException
+ {
+ json(obj, writer, false);
+ }
+
+ public static void json(Object obj, Writer writer, boolean writeClass) throws IOException
+ {
+ if( obj == null )
+ writer.write(NULL);
+ else
+ json(obj, new JSONWriter(writer), writeClass);
+ }
+
+ /**
+ * json string.
+ *
+ * @param obj object.
+ * @param properties property name array.
+ * @return json string.
+ * @throws IOException.
+ */
+ public static String json(Object obj, String[] properties) throws IOException
+ {
+ if( obj == null ) return NULL;
+ StringWriter sw = new StringWriter();
+ try
+ {
+ json(obj, properties, sw);
+ return sw.getBuffer().toString();
+ }
+ finally{ sw.close(); }
+ }
+
+ public static void json(Object obj, final String[] properties, Writer writer) throws IOException
+ {
+ json(obj, properties, writer, false);
+ }
+
+ /**
+ * write json.
+ *
+ * @param obj object.
+ * @param properties property name array.
+ * @param writer writer.
+ * @throws IOException.
+ */
+ public static void json(Object obj, final String[] properties, Writer writer, boolean writeClass) throws IOException
+ {
+ if( obj == null )
+ writer.write(NULL);
+ else
+ json(obj, properties, new JSONWriter(writer), writeClass);
+ }
+
+ private static void json(Object obj, JSONWriter jb, boolean writeClass) throws IOException
+ {
+ if( obj == null )
+ jb.valueNull();
+ else
+ DEFAULT_CONVERTER.writeValue(obj, jb, writeClass);
+ }
+
+ private static void json(Object obj, String[] properties, JSONWriter jb, boolean writeClass) throws IOException
+ {
+ if( obj == null )
+ {
+ jb.valueNull();
+ }
+ else
+ {
+ Wrapper wrapper = Wrapper.getWrapper(obj.getClass());
+
+ Object value;
+ jb.objectBegin();
+ for( String prop : properties )
+ {
+ jb.objectItem(prop);
+ value = wrapper.getPropertyValue(obj, prop);
+ if( value == null )
+ jb.valueNull();
+ else
+ DEFAULT_CONVERTER.writeValue(value, jb, writeClass);
+ }
+ jb.objectEnd();
+ }
+ }
+
+ /**
+ * parse json.
+ *
+ * @param json json source.
+ * @return JSONObject or JSONArray or Boolean or Long or Double or String or null
+ * @throws ParseException
+ */
+ public static Object parse(String json) throws ParseException
+ {
+ StringReader reader = new StringReader(json);
+ try{ return parse(reader); }
+ catch(IOException e){ throw new ParseException(e.getMessage()); }
+ finally{ reader.close(); }
+ }
+
+ /**
+ * parse json.
+ *
+ * @param reader reader.
+ * @return JSONObject or JSONArray or Boolean or Long or Double or String or null
+ * @throws IOException
+ * @throws ParseException
+ */
+ public static Object parse(Reader reader) throws IOException, ParseException
+ {
+ return parse(reader, JSONToken.ANY);
+ }
+
+ /**
+ * parse json.
+ *
+ * @param json json string.
+ * @param type target type.
+ * @return result.
+ * @throws ParseException
+ */
+ public static <T> T parse(String json, Class<T> type) throws ParseException
+ {
+ StringReader reader = new StringReader(json);
+ try{ return parse(reader, type); }
+ catch(IOException e){ throw new ParseException(e.getMessage()); }
+ finally{ reader.close(); }
+ }
+
+ /**
+ * parse json
+ *
+ * @param reader json source.
+ * @param type target type.
+ * @return result.
+ * @throws IOException
+ * @throws ParseException
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T parse(Reader reader, Class<T> type) throws IOException, ParseException
+ {
+ return (T)parse(reader, new J2oVisitor(type, DEFAULT_CONVERTER), JSONToken.ANY);
+ }
+
+ /**
+ * parse json.
+ *
+ * @param json json string.
+ * @param types target type array.
+ * @return result.
+ * @throws ParseException
+ */
+ public static Object[] parse(String json, Class<?>[] types) throws ParseException
+ {
+ StringReader reader = new StringReader(json);
+ try{ return (Object[])parse(reader, types); }
+ catch(IOException e){ throw new ParseException(e.getMessage()); }
+ finally{ reader.close(); }
+ }
+
+ /**
+ * parse json.
+ *
+ * @param reader json source.
+ * @param types target type array.
+ * @return result.
+ * @throws IOException
+ * @throws ParseException
+ */
+ public static Object[] parse(Reader reader, Class<?>[] types) throws IOException, ParseException
+ {
+ return (Object[])parse(reader, new J2oVisitor(types, DEFAULT_CONVERTER), JSONToken.LSQUARE);
+ }
+
+ /**
+ * parse json.
+ *
+ * @param json json string.
+ * @param handler handler.
+ * @return result.
+ * @throws ParseException
+ */
+ public static Object parse(String json, JSONVisitor handler) throws ParseException
+ {
+ StringReader reader = new StringReader(json);
+ try{ return parse(reader, handler); }
+ catch(IOException e){ throw new ParseException(e.getMessage()); }
+ finally{ reader.close(); }
+ }
+
+ /**
+ * parse json.
+ *
+ * @param reader json source.
+ * @param handler handler.
+ * @return resule.
+ * @throws IOException
+ * @throws ParseException
+ */
+ public static Object parse(Reader reader, JSONVisitor handler) throws IOException, ParseException
+ {
+ return parse(reader, handler, JSONToken.ANY);
+ }
+
+ private static Object parse(Reader reader, int expect) throws IOException, ParseException
+ {
+ JSONReader jr = new JSONReader(reader);
+ JSONToken token = jr.nextToken(expect);
+
+ byte state = START;
+ Object value = null, tmp;
+ Stack<Entry> stack = new Stack<Entry>();
+
+ do
+ {
+ switch( state )
+ {
+ case END:
+ throw new ParseException("JSON source format error.");
+ case START:
+ {
+ switch( token.type )
+ {
+ case JSONToken.NULL: case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:
+ {
+ state = END;
+ value = token.value;
+ break;
+ }
+ case JSONToken.LSQUARE:
+ {
+ state = ARRAY_ITEM;
+ value = new JSONArray();
+ break;
+ }
+ case JSONToken.LBRACE:
+ {
+ state = OBJECT_ITEM;
+ value = new JSONObject();
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");
+ }
+ break;
+ }
+ case ARRAY_ITEM:
+ {
+ switch( token.type )
+ {
+ case JSONToken.COMMA:
+ break;
+ case JSONToken.NULL: case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:
+ {
+ ((JSONArray)value).add(token.value);
+ break;
+ }
+ case JSONToken.RSQUARE: // end of array.
+ {
+ if( stack.isEmpty() )
+ {
+ state = END;
+ }
+ else
+ {
+ Entry entry = stack.pop();
+ state = entry.state;
+ value = entry.value;
+ }
+ break;
+ }
+ case JSONToken.LSQUARE: // array begin.
+ {
+ tmp = new JSONArray();
+ ((JSONArray)value).add(tmp);
+ stack.push(new Entry(state, value));
+
+ state = ARRAY_ITEM;
+ value = tmp;
+ break;
+ }
+ case JSONToken.LBRACE: // object begin.
+ {
+ tmp = new JSONObject();
+ ((JSONArray)value).add(tmp);
+ stack.push(new Entry(state, value));
+
+ state = OBJECT_ITEM;
+ value = tmp;
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");
+ }
+ break;
+ }
+ case OBJECT_ITEM:
+ {
+ switch( token.type )
+ {
+ case JSONToken.COMMA:
+ break;
+ case JSONToken.IDENT: // item name.
+ {
+ stack.push(new Entry(OBJECT_ITEM, (String)token.value));
+ state = OBJECT_VALUE;
+ break;
+ }
+ case JSONToken.NULL:
+ {
+ stack.push(new Entry(OBJECT_ITEM, "null"));
+ state = OBJECT_VALUE;
+ break;
+ }
+ case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:
+ {
+ stack.push(new Entry(OBJECT_ITEM, token.value.toString()));
+ state = OBJECT_VALUE;
+ break;
+ }
+ case JSONToken.RBRACE: // end of object.
+ {
+ if( stack.isEmpty() )
+ {
+ state = END;
+ }
+ else
+ {
+ Entry entry = stack.pop();
+ state = entry.state;
+ value = entry.value;
+ }
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'");
+ }
+ break;
+ }
+ case OBJECT_VALUE:
+ {
+ switch( token.type )
+ {
+ case JSONToken.COLON:
+ break;
+ case JSONToken.NULL: case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:
+ {
+ ((JSONObject)value).put((String)stack.pop().value, token.value);
+ state = OBJECT_ITEM;
+ break;
+ }
+ case JSONToken.LSQUARE: // array begin.
+ {
+ tmp = new JSONArray();
+ ((JSONObject)value).put((String)stack.pop().value, tmp);
+ stack.push(new Entry(OBJECT_ITEM, value));
+
+ state = ARRAY_ITEM;
+ value = tmp;
+ break;
+ }
+ case JSONToken.LBRACE: // object begin.
+ {
+ tmp = new JSONObject();
+ ((JSONObject)value).put((String)stack.pop().value, tmp);
+ stack.push(new Entry(OBJECT_ITEM, value));
+
+ state = OBJECT_ITEM;
+ value = tmp;
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");
+ }
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted state.");
+ }
+ }
+ while( ( token = jr.nextToken() ) != null );
+ stack.clear();
+ return value;
+ }
+
+ private static Object parse(Reader reader, JSONVisitor handler, int expect) throws IOException, ParseException
+ {
+ JSONReader jr = new JSONReader(reader);
+ JSONToken token = jr.nextToken(expect);
+
+ Object value = null;
+ int state = START, index = 0;
+ Stack<int[]> states = new Stack<int[]>();
+ boolean pv = false;
+
+ handler.begin();
+ do
+ {
+ switch( state )
+ {
+ case END:
+ throw new ParseException("JSON source format error.");
+ case START:
+ {
+ switch( token.type )
+ {
+ case JSONToken.NULL:
+ {
+ value = token.value;
+ state = END;
+ pv = true;
+ break;
+ }
+ case JSONToken.BOOL:
+ {
+ value = token.value;
+ state = END;
+ pv = true;
+ break;
+ }
+ case JSONToken.INT:
+ {
+ value = token.value;
+ state = END;
+ pv = true;
+ break;
+ }
+ case JSONToken.FLOAT:
+ {
+ value = token.value;
+ state = END;
+ pv = true;
+ break;
+ }
+ case JSONToken.STRING:
+ {
+ value = token.value;
+ state = END;
+ pv = true;
+ break;
+ }
+ case JSONToken.LSQUARE:
+ {
+ handler.arrayBegin();
+ state = ARRAY_ITEM;
+ break;
+ }
+ case JSONToken.LBRACE:
+ {
+ handler.objectBegin();
+ state = OBJECT_ITEM;
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");
+ }
+ break;
+ }
+ case ARRAY_ITEM:
+ {
+ switch( token.type )
+ {
+ case JSONToken.COMMA:
+ break;
+ case JSONToken.NULL:
+ {
+ handler.arrayItem(index++);
+ handler.arrayItemValue(index, token.value, true);
+ break;
+ }
+ case JSONToken.BOOL:
+ {
+ handler.arrayItem(index++);
+ handler.arrayItemValue(index, token.value, true);
+ break;
+ }
+ case JSONToken.INT:
+ {
+ handler.arrayItem(index++);
+ handler.arrayItemValue(index, token.value, true);
+ break;
+ }
+ case JSONToken.FLOAT:
+ {
+ handler.arrayItem(index++);
+ handler.arrayItemValue(index, token.value, true);
+ break;
+ }
+ case JSONToken.STRING:
+ {
+ handler.arrayItem(index++);
+ handler.arrayItemValue(index, token.value, true);
+ break;
+ }
+ case JSONToken.LSQUARE:
+ {
+ handler.arrayItem(index++);
+ states.push(new int[]{state, index});
+
+ index = 0;
+ state = ARRAY_ITEM;
+ handler.arrayBegin();
+ break;
+ }
+ case JSONToken.LBRACE:
+ {
+ handler.arrayItem(index++);
+ states.push(new int[]{state, index});
+
+ index = 0;
+ state = OBJECT_ITEM;
+ handler.objectBegin();
+ break;
+ }
+ case JSONToken.RSQUARE:
+ {
+ if( states.isEmpty() )
+ {
+ value = handler.arrayEnd(index);
+ state = END;
+ }
+ else
+ {
+ value = handler.arrayEnd(index);
+ int[] tmp = states.pop();
+ state = tmp[0];
+ index = tmp[1];
+
+ switch( state )
+ {
+ case ARRAY_ITEM:
+ {
+ handler.arrayItemValue(index, value, false);
+ break;
+ }
+ case OBJECT_ITEM:
+ {
+ handler.objectItemValue(value, false);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");
+ }
+ break;
+ }
+ case OBJECT_ITEM:
+ {
+ switch( token.type )
+ {
+ case JSONToken.COMMA:
+ break;
+ case JSONToken.IDENT:
+ {
+ handler.objectItem((String)token.value);
+ state = OBJECT_VALUE;
+ break;
+ }
+ case JSONToken.NULL:
+ {
+ handler.objectItem("null");
+ state = OBJECT_VALUE;
+ break;
+ }
+ case JSONToken.BOOL: case JSONToken.INT: case JSONToken.FLOAT: case JSONToken.STRING:
+ {
+ handler.objectItem(token.value.toString());
+ state = OBJECT_VALUE;
+ break;
+ }
+ case JSONToken.RBRACE:
+ {
+ if( states.isEmpty() )
+ {
+ value = handler.objectEnd(index);
+ state = END;
+ }
+ else
+ {
+ value = handler.objectEnd(index);
+ int[] tmp = states.pop();
+ state = tmp[0];
+ index = tmp[1];
+
+ switch( state )
+ {
+ case ARRAY_ITEM:
+ {
+ handler.arrayItemValue(index, value, false);
+ break;
+ }
+ case OBJECT_ITEM:
+ {
+ handler.objectItemValue(value, false);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'");
+ }
+ break;
+ }
+ case OBJECT_VALUE:
+ {
+ switch( token.type )
+ {
+ case JSONToken.COLON:
+ break;
+ case JSONToken.NULL:
+ {
+ handler.objectItemValue(token.value, true);
+ state = OBJECT_ITEM;
+ break;
+ }
+ case JSONToken.BOOL:
+ {
+ handler.objectItemValue(token.value, true);
+ state = OBJECT_ITEM;
+ break;
+ }
+ case JSONToken.INT:
+ {
+ handler.objectItemValue(token.value, true);
+ state = OBJECT_ITEM;
+ break;
+ }
+ case JSONToken.FLOAT:
+ {
+ handler.objectItemValue(token.value, true);
+ state = OBJECT_ITEM;
+ break;
+ }
+ case JSONToken.STRING:
+ {
+ handler.objectItemValue(token.value, true);
+ state = OBJECT_ITEM;
+ break;
+ }
+ case JSONToken.LSQUARE:
+ {
+ states.push(new int[]{OBJECT_ITEM, index});
+
+ index = 0;
+ state = ARRAY_ITEM;
+ handler.arrayBegin();
+ break;
+ }
+ case JSONToken.LBRACE:
+ {
+ states.push(new int[]{OBJECT_ITEM, index});
+
+ index = 0;
+ state = OBJECT_ITEM;
+ handler.objectBegin();
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'");
+ }
+ break;
+ }
+ default:
+ throw new ParseException("Unexcepted state.");
+ }
+ }
+ while( ( token = jr.nextToken() ) != null );
+ states.clear();
+ return handler.end(value, pv);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONArray.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONArray.java
new file mode 100644
index 0000000..e25ef9b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONArray.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * JSONArray.
+ *
+ * @author qian.lei
+ */
+
+public class JSONArray implements JSONNode
+{
+ private List<Object> mArray = new ArrayList<Object>();
+
+ /**
+ * get.
+ *
+ * @param index index.
+ * @return boolean or long or double or String or JSONArray or JSONObject or null.
+ */
+ public Object get(int index)
+ {
+ return mArray.get(index);
+ }
+
+ /**
+ * get boolean value.
+ *
+ * @param index index.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public boolean getBoolean(int index, boolean def)
+ {
+ Object tmp = mArray.get(index);
+ return tmp != null && tmp instanceof Boolean ? ((Boolean)tmp).booleanValue() : def;
+ }
+
+ /**
+ * get int value.
+ *
+ * @param index index.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public int getInt(int index, int def)
+ {
+ Object tmp = mArray.get(index);
+ return tmp != null && tmp instanceof Number ? ((Number)tmp).intValue() : def;
+ }
+
+ /**
+ * get long value.
+ *
+ * @param index index.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public long getLong(int index, long def)
+ {
+ Object tmp = mArray.get(index);
+ return tmp != null && tmp instanceof Number ? ((Number)tmp).longValue() : def;
+ }
+
+ /**
+ * get float value.
+ *
+ * @param index index.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public float getFloat(int index, float def)
+ {
+ Object tmp = mArray.get(index);
+ return tmp != null && tmp instanceof Number ? ((Number)tmp).floatValue() : def;
+ }
+
+ /**
+ * get double value.
+ *
+ * @param index index.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public double getDouble(int index, double def)
+ {
+ Object tmp = mArray.get(index);
+ return tmp != null && tmp instanceof Number ? ((Number)tmp).doubleValue() : def;
+ }
+
+ /**
+ * get string value.
+ *
+ * @param index index.
+ * @return value or default value.
+ */
+ public String getString(int index)
+ {
+ Object tmp = mArray.get(index);
+ return tmp == null ? null : tmp.toString();
+ }
+
+ /**
+ * get JSONArray value.
+ *
+ * @param index index.
+ * @return value or default value.
+ */
+ public JSONArray getArray(int index)
+ {
+ Object tmp = mArray.get(index);
+ return tmp == null ? null : tmp instanceof JSONArray ? (JSONArray)tmp : null;
+ }
+
+ /**
+ * get JSONObject value.
+ *
+ * @param index index.
+ * @return value or default value.
+ */
+ public JSONObject getObject(int index)
+ {
+ Object tmp = mArray.get(index);
+ return tmp == null ? null : tmp instanceof JSONObject ? (JSONObject)tmp : null;
+ }
+
+ /**
+ * get array length.
+ *
+ * @return length.
+ */
+ public int length()
+ {
+ return mArray.size();
+ }
+
+ /**
+ * add item.
+ */
+ public void add(Object ele)
+ {
+ mArray.add(ele);
+ }
+
+ /**
+ * add items.
+ */
+ public void addAll(Object[] eles)
+ {
+ for( Object ele : eles )
+ mArray.add(ele);
+ }
+
+ /**
+ * add items.
+ */
+ public void addAll(Collection<?> c)
+ {
+ mArray.addAll(c);
+ }
+
+ /**
+ * write json.
+ *
+ * @param jc json converter
+ * @param jb json builder.
+ */
+ public void writeJSON(JSONConverter jc, JSONWriter jb, boolean writeClass) throws IOException
+ {
+ jb.arrayBegin();
+ for( Object item : mArray )
+ {
+ if( item == null )
+ jb.valueNull();
+ else
+ jc.writeValue(item, jb, writeClass);
+ }
+ jb.arrayEnd();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONConverter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONConverter.java
new file mode 100644
index 0000000..921460a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONConverter.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+
+/**
+ * JSON converter.
+ *
+ * @author qianlei
+ */
+
+public interface JSONConverter
+{
+ /**
+ * write object.
+ *
+ * @param obj obj.
+ * @param builder builder.
+ * @throws IOException
+ */
+ void writeValue(Object obj, JSONWriter builder, boolean writeClass) throws IOException;
+
+ /**
+ * convert json value to target class.
+ *
+ * @param type target type.
+ * @param jv json value.
+ * @return target object.
+ * @throws IOException.
+ */
+ Object readValue(Class<?> type, Object jv) throws IOException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONNode.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONNode.java
new file mode 100644
index 0000000..131f8b0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONNode.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+
+/**
+ * JSONSerializable.
+ *
+ * @author qian.lei
+ */
+
+interface JSONNode
+{
+ /**
+ * write json string.
+ *
+ * @param jc json converter.
+ * @param jb json builder.
+ * @throws IOException
+ */
+ void writeJSON(JSONConverter jc, JSONWriter jb, boolean writeClass) throws IOException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONObject.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONObject.java
new file mode 100644
index 0000000..5ca2172
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONObject.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * JSONObject.
+ *
+ * @author qian.lei
+ */
+
+public class JSONObject implements JSONNode
+{
+ private Map<String,Object> mMap = new HashMap<String,Object>();
+
+ /**
+ * get.
+ *
+ * @param key key.
+ * @return boolean or long or double or String or JSONArray or JSONObject or null.
+ */
+ public Object get(String key)
+ {
+ return mMap.get(key);
+ }
+
+ /**
+ * get boolean value.
+ *
+ * @param key key.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public boolean getBoolean(String key, boolean def)
+ {
+ Object tmp = mMap.get(key);
+ return tmp != null && tmp instanceof Boolean ? (Boolean)tmp : def;
+ }
+
+ /**
+ * get int value.
+ *
+ * @param key key.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public int getInt(String key, int def)
+ {
+ Object tmp = mMap.get(key);
+ return tmp != null && tmp instanceof Number ? ((Number)tmp).intValue() : def;
+ }
+
+ /**
+ * get long value.
+ *
+ * @param key key.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public long getLong(String key, long def)
+ {
+ Object tmp = mMap.get(key);
+ return tmp != null && tmp instanceof Number ? ((Number)tmp).longValue() : def;
+ }
+
+ /**
+ * get float value.
+ *
+ * @param key key.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public float getFloat(String key, float def)
+ {
+ Object tmp = mMap.get(key);
+ return tmp != null && tmp instanceof Number ? ((Number)tmp).floatValue() : def;
+ }
+
+ /**
+ * get double value.
+ *
+ * @param key key.
+ * @param def default value.
+ * @return value or default value.
+ */
+ public double getDouble(String key, double def)
+ {
+ Object tmp = mMap.get(key);
+ return tmp != null && tmp instanceof Number ? ((Number)tmp).doubleValue() : def;
+ }
+
+ /**
+ * get string value.
+ *
+ * @param key key.
+ * @return value or default value.
+ */
+ public String getString(String key)
+ {
+ Object tmp = mMap.get(key);
+ return tmp == null ? null : tmp.toString();
+ }
+
+ /**
+ * get JSONArray value.
+ *
+ * @param key key.
+ * @return value or default value.
+ */
+ public JSONArray getArray(String key)
+ {
+ Object tmp = mMap.get(key);
+ return tmp == null ? null : tmp instanceof JSONArray ? (JSONArray)tmp : null;
+ }
+
+ /**
+ * get JSONObject value.
+ *
+ * @param key key.
+ * @return value or default value.
+ */
+ public JSONObject getObject(String key)
+ {
+ Object tmp = mMap.get(key);
+ return tmp == null ? null : tmp instanceof JSONObject ? (JSONObject)tmp : null;
+ }
+
+ /**
+ * get key iterator.
+ *
+ * @return key iterator.
+ */
+ public Iterator<String> keys()
+ {
+ return mMap.keySet().iterator();
+ }
+
+ /**
+ * contains key.
+ *
+ * @param key key.
+ * @return contains or not.
+ */
+ public boolean contains(String key)
+ {
+ return mMap.containsKey(key);
+ }
+
+ /**
+ * put value.
+ *
+ * @param name name.
+ * @param value value.
+ */
+ public void put(String name, Object value)
+ {
+ mMap.put(name, value);
+ }
+
+ /**
+ * put all.
+ *
+ * @param names name array.
+ * @param values value array.
+ */
+ public void putAll(String[] names, Object[] values)
+ {
+ for(int i=0,len=Math.min(names.length, values.length);i<len;i++)
+ mMap.put(names[i], values[i]);
+ }
+
+ /**
+ * put all.
+ *
+ * @param map map.
+ */
+ public void putAll(Map<String, Object> map)
+ {
+ for( Map.Entry<String, Object> entry : map.entrySet() )
+ mMap.put(entry.getKey(), entry.getValue());
+ }
+
+ /**
+ * write json.
+ *
+ * @param jc json converter.
+ * @param jb json builder.
+ */
+ public void writeJSON(JSONConverter jc, JSONWriter jb, boolean writeClass) throws IOException
+ {
+ String key;
+ Object value;
+ jb.objectBegin();
+ for( Map.Entry<String,Object> entry : mMap.entrySet() )
+ {
+ key = entry.getKey();
+ jb.objectItem(key);
+ value = entry.getValue();
+ if( value == null )
+ jb.valueNull();
+ else
+ jc.writeValue(value, jb, writeClass);
+ }
+ jb.objectEnd();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONReader.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONReader.java
new file mode 100644
index 0000000..f32a721
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONReader.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * JSON reader.
+ *
+ * @author qian.lei
+ */
+
+public class JSONReader
+{
+ private static ThreadLocal<Yylex> LOCAL_LEXER = new ThreadLocal<Yylex>(){};
+
+ private Yylex mLex;
+
+ public JSONReader(InputStream is, String charset) throws UnsupportedEncodingException
+ {
+ this(new InputStreamReader(is, charset));
+ }
+
+ public JSONReader(Reader reader)
+ {
+ mLex = getLexer(reader);
+ }
+
+ public JSONToken nextToken() throws IOException, ParseException
+ {
+ return mLex.yylex();
+ }
+
+ public JSONToken nextToken(int expect) throws IOException, ParseException
+ {
+ JSONToken ret = mLex.yylex();
+ if( ret == null )
+ throw new ParseException("EOF error.");
+ if( expect != JSONToken.ANY && expect != ret.type )
+ throw new ParseException("Unexcepted token.");
+ return ret;
+ }
+
+ private static Yylex getLexer(Reader reader)
+ {
+ Yylex ret = LOCAL_LEXER.get();
+ if( ret == null )
+ {
+ ret = new Yylex(reader);
+ LOCAL_LEXER.set(ret);
+ }
+ else
+ {
+ ret.yyreset(reader);
+ }
+ return ret;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONToken.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONToken.java
new file mode 100644
index 0000000..b3c925d
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONToken.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+/**
+ * JSONToken.
+ *
+ * @author qian.lei
+ */
+
+public class JSONToken
+{
+ // token type
+ public static final int ANY = 0, IDENT = 0x01, LBRACE = 0x02, LSQUARE = 0x03, RBRACE = 0x04, RSQUARE = 0x05, COMMA = 0x06, COLON = 0x07;
+
+ public static final int NULL = 0x10, BOOL = 0x11, INT = 0x12, FLOAT = 0x13, STRING = 0x14, ARRAY = 0x15, OBJECT = 0x16;
+
+ public final int type;
+
+ public final Object value;
+
+ JSONToken(int t)
+ {
+ this(t, null);
+ }
+
+ JSONToken(int t, Object v)
+ {
+ type = t;
+ value = v;
+ }
+
+ static String token2string(int t)
+ {
+ switch( t )
+ {
+ case LBRACE: return "{";
+ case RBRACE: return "}";
+ case LSQUARE: return "[";
+ case RSQUARE: return "]";
+ case COMMA: return ",";
+ case COLON: return ":";
+ case IDENT: return "IDENT";
+ case NULL: return "NULL";
+ case BOOL: return "BOOL VALUE";
+ case INT: return "INT VALUE";
+ case FLOAT: return "FLOAT VALUE";
+ case STRING: return "STRING VALUE";
+ default: return "ANY";
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONVisitor.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONVisitor.java
new file mode 100644
index 0000000..d04f531
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONVisitor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+/**
+ * JSONVisitor.
+ *
+ * @author qian.lei
+ */
+
+public interface JSONVisitor
+{
+ public static final String CLASS_PROPERTY = "class";
+
+ /**
+ * parse begin .
+ */
+ void begin();
+
+ /**
+ * parse end.
+ *
+ * @param obj root obj.
+ * @param isValue is json value.
+ * @return parse result.
+ * @throws ParseException
+ */
+ Object end(Object obj, boolean isValue) throws ParseException;
+
+ /**
+ * object begin.
+ *
+ * @throws ParseException
+ */
+ void objectBegin() throws ParseException;
+
+ /**
+ * object end, return object value.
+ *
+ * @param count property count.
+ * @return object value.
+ * @throws ParseException
+ */
+ Object objectEnd(int count) throws ParseException;
+
+ /**
+ * object property name.
+ *
+ * @param name name.
+ * @throws ParseException
+ */
+ void objectItem(String name) throws ParseException;
+
+ /**
+ * object property value.
+ *
+ * @param obj obj.
+ * @param isValue is json value.
+ * @throws ParseException
+ */
+ void objectItemValue(Object obj, boolean isValue) throws ParseException;
+
+ /**
+ * array begin.
+ *
+ * @throws ParseException
+ */
+ void arrayBegin() throws ParseException;
+
+ /**
+ * array end, return array value.
+ *
+ * @param count count.
+ * @return array value.
+ * @throws ParseException
+ */
+ Object arrayEnd(int count) throws ParseException;
+
+ /**
+ * array item.
+ *
+ * @param index index.
+ * @throws ParseException
+ */
+ void arrayItem(int index) throws ParseException;
+
+ /**
+ * array item.
+ *
+ * @param index index.
+ * @param obj item.
+ * @param isValue is json value.
+ * @throws ParseException
+ */
+ void arrayItemValue(int index, Object obj, boolean isValue) throws ParseException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONWriter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONWriter.java
new file mode 100644
index 0000000..9d90bf0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONWriter.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+import com.alibaba.dubbo.common.utils.Stack;
+
+/**
+ * JSON Writer.
+ *
+ * w.objectBegin().objectItem("name").valueString("qianlei").objectEnd() = {name:"qianlei"}.
+ *
+ * @author qian.lei
+ */
+
+public class JSONWriter
+{
+ private static final byte UNKNOWN = 0, ARRAY = 1, OBJECT = 2, OBJECT_VALUE = 3;
+
+ private static class State
+ {
+ private byte type;
+ private int itemCount = 0;
+
+ State(byte t){ type = t; }
+ }
+
+ private Writer mWriter;
+
+ private State mState = new State(UNKNOWN);
+
+ private Stack<State> mStack = new Stack<State>();
+
+ public JSONWriter(Writer writer)
+ {
+ mWriter = writer;
+ }
+
+ public JSONWriter(OutputStream is, String charset) throws UnsupportedEncodingException
+ {
+ mWriter = new OutputStreamWriter(is, charset);
+ }
+
+ /**
+ * object begin.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter objectBegin() throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(JSON.LBRACE);
+ mStack.push(mState);
+ mState = new State(OBJECT);
+ return this;
+ }
+
+ /**
+ * object end.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter objectEnd() throws IOException
+ {
+ mWriter.write(JSON.RBRACE);
+ mState = mStack.pop();
+ return this;
+ }
+
+ /**
+ * object item.
+ *
+ * @param name name.
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter objectItem(String name) throws IOException
+ {
+ beforeObjectItem();
+
+ mWriter.write(JSON.QUOTE);
+ mWriter.write(escape(name));
+ mWriter.write(JSON.QUOTE);
+ mWriter.write(JSON.COLON);
+ return this;
+ }
+
+ /**
+ * array begin.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter arrayBegin() throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(JSON.LSQUARE);
+ mStack.push(mState);
+ mState = new State(ARRAY);
+ return this;
+ }
+
+ /**
+ * array end, return array value.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter arrayEnd() throws IOException
+ {
+ mWriter.write(JSON.RSQUARE);
+ mState = mStack.pop();
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter valueNull() throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(JSON.NULL);
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueString(String value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(JSON.QUOTE);
+ mWriter.write(escape(value));
+ mWriter.write(JSON.QUOTE);
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueBoolean(boolean value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(value ? "true" : "false");
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueInt(int value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(String.valueOf(value));
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueLong(long value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(String.valueOf(value));
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueFloat(float value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(String.valueOf(value));
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueDouble(double value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(String.valueOf(value));
+ return this;
+ }
+
+ private void beforeValue() throws IOException
+ {
+ switch( mState.type )
+ {
+ case ARRAY:
+ if( mState.itemCount++ > 0 )
+ mWriter.write(JSON.COMMA);
+ return;
+ case OBJECT:
+ throw new IOException("Must call objectItem first.");
+ case OBJECT_VALUE:
+ mState.type = OBJECT;
+ return;
+ }
+ }
+
+ private void beforeObjectItem() throws IOException
+ {
+ switch( mState.type )
+ {
+ case OBJECT_VALUE:
+ mWriter.write(JSON.NULL);
+ case OBJECT:
+ mState.type = OBJECT_VALUE;
+ if( mState.itemCount++ > 0 )
+ mWriter.write(JSON.COMMA);
+ return;
+ default:
+ throw new IOException("Must call objectBegin first.");
+ }
+ }
+
+ private static final String[] CONTROL_CHAR_MAP = new String[]{
+ "\\u0000","\\u0001","\\u0002","\\u0003","\\u0004","\\u0005","\\u0006","\\u0007",
+ "\\b","\\t","\\n","\\u000b","\\f","\\r","\\u000e","\\u000f",
+ "\\u0010","\\u0011","\\u0012","\\u0013","\\u0014","\\u0015","\\u0016","\\u0017",
+ "\\u0018","\\u0019","\\u001a","\\u001b","\\u001c","\\u001d","\\u001e","\\u001f"
+ };
+
+ private static String escape(String str)
+ {
+ if( str == null )
+ return str;
+ int len = str.length();
+ if( len == 0 )
+ return str;
+
+ char c;
+ StringBuilder sb = null;
+ for(int i=0;i<len;i++)
+ {
+ c = str.charAt(i);
+ if( c < ' ' ) // control char.
+ {
+ if( sb == null )
+ {
+ sb = new StringBuilder(len<<1);
+ sb.append(str,0,i);
+ }
+ sb.append(CONTROL_CHAR_MAP[c]);
+ }
+ else
+ {
+ switch( c )
+ {
+ case '\\': case '/': case '"':
+ if( sb == null )
+ {
+ sb = new StringBuilder(len<<1);
+ sb.append(str,0,i);
+ }
+ sb.append('\\').append(c);
+ break;
+ default:
+ if( sb != null )
+ sb.append(c);
+ }
+ }
+ }
+ return sb == null ? str : sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/ParseException.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/ParseException.java
new file mode 100644
index 0000000..5bda123
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/ParseException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+/**
+ * ParseException.
+ *
+ * @author qian.lei
+ */
+
+public class ParseException extends Exception
+{
+ private static final long serialVersionUID = 8611884051738966316L;
+
+ public ParseException()
+ {
+ super();
+ }
+
+ public ParseException(String message)
+ {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/Yylex.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/Yylex.java
new file mode 100644
index 0000000..0e3163b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/Yylex.java
@@ -0,0 +1,800 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+/**
+ * This class is a scanner generated by
+ * <a href="http://www.jflex.de/">JFlex</a> 1.4.3
+ * on 7/3/10 3:12 AM from the specification file
+ * <tt>/Users/qianlei/dev/proj/dubbo-1.1/dubbo.common/src/main/java/com/alibaba/dubbo/common/json/json.flex</tt>
+ */
+public class Yylex {
+
+ /** This character denotes the end of file */
+ public static final int YYEOF = -1;
+
+ /** initial size of the lookahead buffer */
+ private static final int ZZ_BUFFERSIZE = 16384;
+
+ /** lexical states */
+ public static final int STR2 = 4;
+ public static final int STR1 = 2;
+ public static final int YYINITIAL = 0;
+
+ /**
+ * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
+ * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
+ * at the beginning of a line
+ * l is of the form l = 2*k, k a non negative integer
+ */
+ private static final int ZZ_LEXSTATE[] = {
+ 0, 0, 1, 1, 2, 2
+ };
+
+ /**
+ * Translates characters to character classes
+ */
+ private static final String ZZ_CMAP_PACKED =
+ "\11\0\1\13\1\13\2\0\1\13\22\0\1\13\1\0\1\10\1\0"+
+ "\1\2\2\0\1\11\3\0\1\7\1\43\1\4\1\5\1\14\12\1"+
+ "\1\44\6\0\1\33\3\3\1\6\1\32\5\2\1\34\1\2\1\36"+
+ "\3\2\1\25\1\35\1\24\1\26\5\2\1\41\1\12\1\42\1\0"+
+ "\1\2\1\0\1\27\1\15\2\3\1\23\1\16\5\2\1\30\1\2"+
+ "\1\17\3\2\1\20\1\31\1\21\1\22\5\2\1\37\1\0\1\40"+
+ "\uff82\0";
+
+ /**
+ * Translates characters to character classes
+ */
+ private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED);
+
+ /**
+ * Translates DFA states to action switch labels.
+ */
+ private static final int [] ZZ_ACTION = zzUnpackAction();
+
+ private static final String ZZ_ACTION_PACKED_0 =
+ "\3\0\1\1\1\2\1\3\1\1\1\4\1\5\1\6"+
+ "\6\3\1\7\1\10\1\11\1\12\1\13\1\14\1\15"+
+ "\1\16\1\0\1\15\3\0\6\3\1\17\1\20\1\21"+
+ "\1\22\1\23\1\24\1\25\1\26\1\0\1\27\2\30"+
+ "\1\0\6\3\1\0\1\3\1\31\1\32\1\3\1\0"+
+ "\1\33\1\0\1\34";
+
+ private static int [] zzUnpackAction() {
+ int [] result = new int[63];
+ int offset = 0;
+ offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackAction(String packed, int offset, int [] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int count = packed.charAt(i++);
+ int value = packed.charAt(i++);
+ do result[j++] = value; while (--count > 0);
+ }
+ return j;
+ }
+
+
+ /**
+ * Translates a state to a row index in the transition table
+ */
+ private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
+
+ private static final String ZZ_ROWMAP_PACKED_0 =
+ "\0\0\0\45\0\112\0\157\0\224\0\271\0\336\0\157"+
+ "\0\157\0\u0103\0\u0128\0\u014d\0\u0172\0\u0197\0\u01bc\0\u01e1"+
+ "\0\157\0\157\0\157\0\157\0\157\0\157\0\u0206\0\157"+
+ "\0\u022b\0\u0250\0\u0275\0\u029a\0\u02bf\0\u02e4\0\u0309\0\u032e"+
+ "\0\u0353\0\u0378\0\u039d\0\157\0\157\0\157\0\157\0\157"+
+ "\0\157\0\157\0\157\0\u03c2\0\157\0\u03e7\0\u040c\0\u040c"+
+ "\0\u0431\0\u0456\0\u047b\0\u04a0\0\u04c5\0\u04ea\0\u050f\0\u0534"+
+ "\0\271\0\271\0\u0559\0\u057e\0\271\0\u05a3\0\157";
+
+ private static int [] zzUnpackRowMap() {
+ int [] result = new int[63];
+ int offset = 0;
+ offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackRowMap(String packed, int offset, int [] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int high = packed.charAt(i++) << 16;
+ result[j++] = high | packed.charAt(i++);
+ }
+ return j;
+ }
+
+ /**
+ * The transition table of the DFA
+ */
+ private static final int ZZ_TRANS [] = {
+ 3, 4, 5, 5, 6, 3, 5, 3, 7, 8,
+ 3, 9, 3, 5, 10, 11, 5, 12, 5, 5,
+ 13, 5, 5, 5, 5, 5, 14, 5, 5, 5,
+ 15, 16, 17, 18, 19, 20, 21, 22, 22, 22,
+ 22, 22, 22, 22, 22, 23, 22, 24, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 23, 26, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,
+ -1, -1, -1, 27, 28, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 28, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 5, 5, 5, -1,
+ -1, 5, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, -1, -1, -1, -1,
+ -1, -1, -1, 4, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 5, 5, 5,
+ -1, -1, 5, -1, -1, -1, -1, -1, -1, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 29,
+ 5, 5, 5, 5, 5, 5, 5, -1, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, -1, -1, 5,
+ -1, -1, -1, -1, -1, -1, 5, 5, 5, 5,
+ 5, 30, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, -1, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, -1, -1, 5, -1, -1, -1,
+ -1, -1, -1, 5, 5, 5, 31, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, -1, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, -1, -1, 5, -1, -1, -1, -1, -1, -1,
+ 5, 5, 5, 5, 5, 5, 5, 5, 32, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, -1, -1,
+ -1, -1, -1, -1, -1, 5, 5, 5, -1, -1,
+ 5, -1, -1, -1, -1, -1, -1, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 33, 5, 5, 5, -1, -1, -1, -1, -1,
+ -1, -1, 5, 5, 5, -1, -1, 5, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 34, 5, 5, 5, 5, 5, 5,
+ 5, 5, -1, -1, -1, -1, -1, -1, 22, 22,
+ 22, 22, 22, 22, 22, 22, -1, 22, -1, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, -1, -1, -1, -1, -1,
+ -1, -1, -1, 35, -1, 36, -1, 37, 38, 39,
+ 40, 41, 42, 43, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, -1, -1, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 44, 36,
+ -1, 37, 38, 39, 40, 41, 42, 43, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 45, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 46, -1, -1, 47, -1, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, -1, -1, 5, -1, -1, -1,
+ -1, -1, -1, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 48, 5, 5, 5, 5, 5,
+ 5, -1, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, -1, -1, 5, -1, -1, -1, -1, -1, -1,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 49, 5, 5, 5, 5, 5, 5, -1, -1,
+ -1, -1, -1, -1, -1, 5, 5, 5, -1, -1,
+ 5, -1, -1, -1, -1, -1, -1, 5, 5, 5,
+ 5, 5, 50, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, -1, -1, -1, -1, -1,
+ -1, -1, 5, 5, 5, -1, -1, 5, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 51, 5, 5, 5, 5, 5, 5,
+ 5, 5, -1, -1, -1, -1, -1, -1, -1, 5,
+ 5, 5, -1, -1, 5, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 52, 5, 5, -1,
+ -1, -1, -1, -1, -1, -1, 5, 5, 5, -1,
+ -1, 5, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 53, 5, 5, -1, -1, -1, -1,
+ -1, -1, -1, 54, -1, 54, -1, -1, 54, -1,
+ -1, -1, -1, -1, -1, 54, 54, -1, -1, -1,
+ -1, 54, -1, -1, -1, 54, -1, -1, 54, 54,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 45, -1, -1, -1, -1, 28, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 28, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 46, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, -1, -1, 5,
+ -1, -1, -1, -1, -1, -1, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 55, 5,
+ 5, 5, 5, 5, -1, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, -1, -1, 5, -1, -1, -1,
+ -1, -1, -1, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 56, 5, 5, 5, 5, 5,
+ 5, -1, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, -1, -1, 5, -1, -1, -1, -1, -1, -1,
+ 5, 5, 5, 5, 5, 5, 57, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, -1, -1,
+ -1, -1, -1, -1, -1, 5, 5, 5, -1, -1,
+ 57, -1, -1, -1, -1, -1, -1, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, -1, -1, -1, -1, -1,
+ -1, -1, 5, 5, 5, -1, -1, 5, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 58, 5, -1, -1, -1, -1, -1, -1, -1, 5,
+ 5, 5, -1, -1, 5, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 56, 5, 5, -1,
+ -1, -1, -1, -1, -1, -1, 59, -1, 59, -1,
+ -1, 59, -1, -1, -1, -1, -1, -1, 59, 59,
+ -1, -1, -1, -1, 59, -1, -1, -1, 59, -1,
+ -1, 59, 59, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 5, 5, 5, -1, -1, 5, -1,
+ -1, -1, -1, -1, -1, 5, 5, 5, 5, 5,
+ 5, 60, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, -1, -1, -1, -1, -1, -1, -1,
+ 5, 5, 5, -1, -1, 60, -1, -1, -1, -1,
+ -1, -1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ -1, -1, -1, -1, -1, -1, -1, 61, -1, 61,
+ -1, -1, 61, -1, -1, -1, -1, -1, -1, 61,
+ 61, -1, -1, -1, -1, 61, -1, -1, -1, 61,
+ -1, -1, 61, 61, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 62, -1, 62, -1, -1, 62,
+ -1, -1, -1, -1, -1, -1, 62, 62, -1, -1,
+ -1, -1, 62, -1, -1, -1, 62, -1, -1, 62,
+ 62, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ /* error codes */
+ private static final int ZZ_UNKNOWN_ERROR = 0;
+ private static final int ZZ_NO_MATCH = 1;
+ private static final int ZZ_PUSHBACK_2BIG = 2;
+
+ /* error messages for the codes above */
+ private static final String ZZ_ERROR_MSG[] = {
+ "Unkown internal scanner error",
+ "Error: could not match input",
+ "Error: pushback value was too large"
+ };
+
+ /**
+ * ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code>
+ */
+ private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
+
+ private static final String ZZ_ATTRIBUTE_PACKED_0 =
+ "\3\0\1\11\3\1\2\11\7\1\6\11\1\1\1\11"+
+ "\1\0\1\1\3\0\6\1\10\11\1\0\1\11\2\1"+
+ "\1\0\6\1\1\0\4\1\1\0\1\1\1\0\1\11";
+
+ private static int [] zzUnpackAttribute() {
+ int [] result = new int[63];
+ int offset = 0;
+ offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackAttribute(String packed, int offset, int [] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int count = packed.charAt(i++);
+ int value = packed.charAt(i++);
+ do result[j++] = value; while (--count > 0);
+ }
+ return j;
+ }
+
+ /** the input device */
+ private java.io.Reader zzReader;
+
+ /** the current state of the DFA */
+ private int zzState;
+
+ /** the current lexical state */
+ private int zzLexicalState = YYINITIAL;
+
+ /** this buffer contains the current text to be matched and is
+ the source of the yytext() string */
+ private char zzBuffer[] = new char[ZZ_BUFFERSIZE];
+
+ /** the textposition at the last accepting state */
+ private int zzMarkedPos;
+
+ /** the current text position in the buffer */
+ private int zzCurrentPos;
+
+ /** startRead marks the beginning of the yytext() string in the buffer */
+ private int zzStartRead;
+
+ /** endRead marks the last character in the buffer, that has been read
+ from input */
+ private int zzEndRead;
+
+ /** number of newlines encountered up to the start of the matched text */
+ //private int yyline;
+
+ /** the number of characters up to the start of the matched text */
+ //private int yychar;
+
+ /**
+ * the number of characters from the last newline up to the start of the
+ * matched text
+ */
+ //private int yycolumn;
+
+ /**
+ * zzAtBOL == true <=> the scanner is currently at the beginning of a line
+ */
+ //private boolean zzAtBOL = true;
+
+ /** zzAtEOF == true <=> the scanner is at the EOF */
+ private boolean zzAtEOF;
+
+ /** denotes if the user-EOF-code has already been executed */
+ //private boolean zzEOFDone;
+
+ /* user code: */
+private StringBuffer sb;
+
+
+ /**
+ * Creates a new scanner
+ * There is also a java.io.InputStream version of this constructor.
+ *
+ * @param in the java.io.Reader to read input from.
+ */
+ Yylex(java.io.Reader in) {
+ this.zzReader = in;
+ }
+
+ /**
+ * Creates a new scanner.
+ * There is also java.io.Reader version of this constructor.
+ *
+ * @param in the java.io.Inputstream to read input from.
+ */
+ Yylex(java.io.InputStream in) {
+ this(new java.io.InputStreamReader(in));
+ }
+
+ /**
+ * Unpacks the compressed character translation table.
+ *
+ * @param packed the packed character translation table
+ * @return the unpacked character translation table
+ */
+ private static char [] zzUnpackCMap(String packed) {
+ char [] map = new char[0x10000];
+ int i = 0; /* index in packed string */
+ int j = 0; /* index in unpacked array */
+ while (i < 122) {
+ int count = packed.charAt(i++);
+ char value = packed.charAt(i++);
+ do map[j++] = value; while (--count > 0);
+ }
+ return map;
+ }
+
+
+ /**
+ * Refills the input buffer.
+ *
+ * @return <code>false</code>, iff there was new input.
+ *
+ * @exception java.io.IOException if any I/O-Error occurs
+ */
+ private boolean zzRefill() throws java.io.IOException {
+
+ /* first: make room (if you can) */
+ if (zzStartRead > 0) {
+ System.arraycopy(zzBuffer, zzStartRead,
+ zzBuffer, 0,
+ zzEndRead-zzStartRead);
+
+ /* translate stored positions */
+ zzEndRead-= zzStartRead;
+ zzCurrentPos-= zzStartRead;
+ zzMarkedPos-= zzStartRead;
+ zzStartRead = 0;
+ }
+
+ /* is the buffer big enough? */
+ if (zzCurrentPos >= zzBuffer.length) {
+ /* if not: blow it up */
+ char newBuffer[] = new char[zzCurrentPos*2];
+ System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length);
+ zzBuffer = newBuffer;
+ }
+
+ /* finally: fill the buffer with new input */
+ int numRead = zzReader.read(zzBuffer, zzEndRead,
+ zzBuffer.length-zzEndRead);
+
+ if (numRead > 0) {
+ zzEndRead+= numRead;
+ return false;
+ }
+ // unlikely but not impossible: read 0 characters, but not at end of stream
+ if (numRead == 0) {
+ int c = zzReader.read();
+ if (c == -1) {
+ return true;
+ } else {
+ zzBuffer[zzEndRead++] = (char) c;
+ return false;
+ }
+ }
+
+ // numRead < 0
+ return true;
+ }
+
+
+ /**
+ * Closes the input stream.
+ */
+ public final void yyclose() throws java.io.IOException {
+ zzAtEOF = true; /* indicate end of file */
+ zzEndRead = zzStartRead; /* invalidate buffer */
+
+ if (zzReader != null)
+ zzReader.close();
+ }
+
+
+ /**
+ * Resets the scanner to read from a new input stream.
+ * Does not close the old reader.
+ *
+ * All internal variables are reset, the old input stream
+ * <b>cannot</b> be reused (internal buffer is discarded and lost).
+ * Lexical state is set to <tt>ZZ_INITIAL</tt>.
+ *
+ * @param reader the new input stream
+ */
+ public final void yyreset(java.io.Reader reader) {
+ zzReader = reader;
+ //zzAtBOL = true;
+ zzAtEOF = false;
+ //zzEOFDone = false;
+ zzEndRead = zzStartRead = 0;
+ zzCurrentPos = zzMarkedPos = 0;
+ //yyline = yychar = yycolumn = 0;
+ zzLexicalState = YYINITIAL;
+ }
+
+
+ /**
+ * Returns the current lexical state.
+ */
+ public final int yystate() {
+ return zzLexicalState;
+ }
+
+
+ /**
+ * Enters a new lexical state
+ *
+ * @param newState the new lexical state
+ */
+ public final void yybegin(int newState) {
+ zzLexicalState = newState;
+ }
+
+
+ /**
+ * Returns the text matched by the current regular expression.
+ */
+ public final String yytext() {
+ return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead );
+ }
+
+
+ /**
+ * Returns the character at position <tt>pos</tt> from the
+ * matched text.
+ *
+ * It is equivalent to yytext().charAt(pos), but faster
+ *
+ * @param pos the position of the character to fetch.
+ * A value from 0 to yylength()-1.
+ *
+ * @return the character at position pos
+ */
+ public final char yycharat(int pos) {
+ return zzBuffer[zzStartRead+pos];
+ }
+
+
+ /**
+ * Returns the length of the matched text region.
+ */
+ public final int yylength() {
+ return zzMarkedPos-zzStartRead;
+ }
+
+
+ /**
+ * Reports an error that occured while scanning.
+ *
+ * In a wellformed scanner (no or only correct usage of
+ * yypushback(int) and a match-all fallback rule) this method
+ * will only be called with things that "Can't Possibly Happen".
+ * If this method is called, something is seriously wrong
+ * (e.g. a JFlex bug producing a faulty scanner etc.).
+ *
+ * Usual syntax/scanner level error handling should be done
+ * in error fallback rules.
+ *
+ * @param errorCode the code of the errormessage to display
+ */
+ private void zzScanError(int errorCode) {
+ String message;
+ try {
+ message = ZZ_ERROR_MSG[errorCode];
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
+ }
+
+ throw new Error(message);
+ }
+
+
+ /**
+ * Pushes the specified amount of characters back into the input stream.
+ *
+ * They will be read again by then next call of the scanning method
+ *
+ * @param number the number of characters to be read again.
+ * This number must not be greater than yylength()!
+ */
+ public void yypushback(int number) {
+ if ( number > yylength() )
+ zzScanError(ZZ_PUSHBACK_2BIG);
+
+ zzMarkedPos -= number;
+ }
+
+
+ /**
+ * Resumes scanning until the next regular expression is matched,
+ * the end of input is encountered or an I/O-Error occurs.
+ *
+ * @return the next token
+ * @exception java.io.IOException if any I/O-Error occurs
+ */
+ public JSONToken yylex() throws java.io.IOException, ParseException {
+ int zzInput;
+ int zzAction;
+
+ // cached fields:
+ int zzCurrentPosL;
+ int zzMarkedPosL;
+ int zzEndReadL = zzEndRead;
+ char [] zzBufferL = zzBuffer;
+ char [] zzCMapL = ZZ_CMAP;
+
+ int [] zzTransL = ZZ_TRANS;
+ int [] zzRowMapL = ZZ_ROWMAP;
+ int [] zzAttrL = ZZ_ATTRIBUTE;
+
+ while (true) {
+ zzMarkedPosL = zzMarkedPos;
+
+ zzAction = -1;
+
+ zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
+
+ zzState = ZZ_LEXSTATE[zzLexicalState];
+
+
+ zzForAction: {
+ while (true) {
+
+ if (zzCurrentPosL < zzEndReadL)
+ zzInput = zzBufferL[zzCurrentPosL++];
+ else if (zzAtEOF) {
+ zzInput = YYEOF;
+ break zzForAction;
+ }
+ else {
+ // store back cached positions
+ zzCurrentPos = zzCurrentPosL;
+ zzMarkedPos = zzMarkedPosL;
+ boolean eof = zzRefill();
+ // get translated positions and possibly new buffer
+ zzCurrentPosL = zzCurrentPos;
+ zzMarkedPosL = zzMarkedPos;
+ zzBufferL = zzBuffer;
+ zzEndReadL = zzEndRead;
+ if (eof) {
+ zzInput = YYEOF;
+ break zzForAction;
+ }
+ else {
+ zzInput = zzBufferL[zzCurrentPosL++];
+ }
+ }
+ int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ];
+ if (zzNext == -1) break zzForAction;
+ zzState = zzNext;
+
+ int zzAttributes = zzAttrL[zzState];
+ if ( (zzAttributes & 1) == 1 ) {
+ zzAction = zzState;
+ zzMarkedPosL = zzCurrentPosL;
+ if ( (zzAttributes & 8) == 8 ) break zzForAction;
+ }
+
+ }
+ }
+
+ // store back cached position
+ zzMarkedPos = zzMarkedPosL;
+
+ switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
+ case 25:
+ { return new JSONToken(JSONToken.NULL, null);
+ }
+ case 29: break;
+ case 13:
+ { sb.append(yytext());
+ }
+ case 30: break;
+ case 18:
+ { sb.append('\b');
+ }
+ case 31: break;
+ case 9:
+ { return new JSONToken(JSONToken.LSQUARE);
+ }
+ case 32: break;
+ case 2:
+ { Long val = Long.valueOf(yytext()); return new JSONToken(JSONToken.INT, val);
+ }
+ case 33: break;
+ case 16:
+ { sb.append('\\');
+ }
+ case 34: break;
+ case 8:
+ { return new JSONToken(JSONToken.RBRACE);
+ }
+ case 35: break;
+ case 26:
+ { return new JSONToken(JSONToken.BOOL, Boolean.TRUE);
+ }
+ case 36: break;
+ case 23:
+ { sb.append('\'');
+ }
+ case 37: break;
+ case 5:
+ { sb = new StringBuffer(); yybegin(STR2);
+ }
+ case 38: break;
+ case 27:
+ { return new JSONToken(JSONToken.BOOL, Boolean.FALSE);
+ }
+ case 39: break;
+ case 12:
+ { return new JSONToken(JSONToken.COLON);
+ }
+ case 40: break;
+ case 21:
+ { sb.append('\r');
+ }
+ case 41: break;
+ case 3:
+ { return new JSONToken(JSONToken.IDENT, yytext());
+ }
+ case 42: break;
+ case 28:
+ { try{ sb.append((char)Integer.parseInt(yytext().substring(2),16)); }catch(Exception e){ throw new ParseException(e.getMessage()); }
+ }
+ case 43: break;
+ case 10:
+ { return new JSONToken(JSONToken.RSQUARE);
+ }
+ case 44: break;
+ case 17:
+ { sb.append('/');
+ }
+ case 45: break;
+ case 11:
+ { return new JSONToken(JSONToken.COMMA);
+ }
+ case 46: break;
+ case 15:
+ { sb.append('"');
+ }
+ case 47: break;
+ case 24:
+ { Double val = Double.valueOf(yytext()); return new JSONToken(JSONToken.FLOAT, val);
+ }
+ case 48: break;
+ case 1:
+ { throw new ParseException("Unexpected char [" + yytext() +"]");
+ }
+ case 49: break;
+ case 19:
+ { sb.append('\f');
+ }
+ case 50: break;
+ case 7:
+ { return new JSONToken(JSONToken.LBRACE);
+ }
+ case 51: break;
+ case 14:
+ { yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString());
+ }
+ case 52: break;
+ case 22:
+ { sb.append('\t');
+ }
+ case 53: break;
+ case 4:
+ { sb = new StringBuffer(); yybegin(STR1);
+ }
+ case 54: break;
+ case 20:
+ { sb.append('\n');
+ }
+ case 55: break;
+ case 6:
+ {
+ }
+ case 56: break;
+ default:
+ if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
+ zzAtEOF = true;
+ return null;
+ }
+ else {
+ zzScanError(ZZ_NO_MATCH);
+ }
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Level.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Level.java
new file mode 100644
index 0000000..060c9ec
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Level.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.logger;
+
+/**
+ * Level
+ *
+ * @author william.liangf
+ */
+public enum Level {
+
+ /**
+ * ALL
+ */
+ ALL,
+
+ /**
+ * TRACE
+ */
+ TRACE,
+
+ /**
+ * DEBUG
+ */
+ DEBUG,
+
+ /**
+ * INFO
+ */
+ INFO,
+
+ /**
+ * WARN
+ */
+ WARN,
+
+ /**
+ * ERROR
+ */
+ ERROR,
+
+ /**
+ * OFF
+ */
+ OFF
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Logger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Logger.java
new file mode 100644
index 0000000..d7d71cd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Logger.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.logger;
+
+/**
+ * 日志接口 <p/> 声明:引用自commons-logging
+ *
+ * @author william.liangf
+ */
+public interface Logger {
+
+ /**
+ * 输出跟踪信息
+ *
+ * @param msg 信息内容
+ */
+ public void trace(String msg);
+
+ /**
+ * 输出跟踪信息
+ *
+ * @param e 异常信息
+ */
+ public void trace(Throwable e);
+
+ /**
+ * 输出跟踪信息
+ *
+ * @param msg 信息内容
+ * @param e 异常信息
+ */
+ public void trace(String msg, Throwable e);
+
+ /**
+ * 输出调试信息
+ *
+ * @param msg 信息内容
+ */
+ public void debug(String msg);
+
+ /**
+ * 输出调试信息
+ *
+ * @param e 异常信息
+ */
+ public void debug(Throwable e);
+
+ /**
+ * 输出调试信息
+ *
+ * @param msg 信息内容
+ * @param e 异常信息
+ */
+ public void debug(String msg, Throwable e);
+
+ /**
+ * 输出普通信息
+ *
+ * @param msg 信息内容
+ */
+ public void info(String msg);
+
+ /**
+ * 输出普通信息
+ *
+ * @param e 异常信息
+ */
+ public void info(Throwable e);
+
+ /**
+ * 输出普通信息
+ *
+ * @param msg 信息内容
+ * @param e 异常信息
+ */
+ public void info(String msg, Throwable e);
+
+ /**
+ * 输出警告信息
+ *
+ * @param msg 信息内容
+ */
+ public void warn(String msg);
+
+ /**
+ * 输出警告信息
+ *
+ * @param e 异常信息
+ */
+ public void warn(Throwable e);
+
+ /**
+ * 输出警告信息
+ *
+ * @param msg 信息内容
+ * @param e 异常信息
+ */
+ public void warn(String msg, Throwable e);
+
+ /**
+ * 输出错误信息
+ *
+ * @param msg 信息内容
+ */
+ public void error(String msg);
+
+ /**
+ * 输出错误信息
+ *
+ * @param e 异常信息
+ */
+ public void error(Throwable e);
+
+ /**
+ * 输出错误信息
+ *
+ * @param msg 信息内容
+ * @param e 异常信息
+ */
+ public void error(String msg, Throwable e);
+
+ /**
+ * 跟踪信息是否开启
+ *
+ * @return 是否开启
+ */
+ public boolean isTraceEnabled();
+
+ /**
+ * 调试信息是否开启
+ *
+ * @return 是否开启
+ */
+ public boolean isDebugEnabled();
+
+ /**
+ * 普通信息是否开启
+ *
+ * @return 是否开启
+ */
+ public boolean isInfoEnabled();
+
+ /**
+ * 警告信息是否开启
+ *
+ * @return 是否开启
+ */
+ public boolean isWarnEnabled();
+
+ /**
+ * 错误信息是否开启
+ *
+ * @return 是否开启
+ */
+ public boolean isErrorEnabled();
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerAdapter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerAdapter.java
new file mode 100644
index 0000000..b83e365
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerAdapter.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.common.logger;
+
+import java.io.File;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * 日志输出器供给器
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface LoggerAdapter {
+
+ /**
+ * 获取日志输出器
+ *
+ * @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/LoggerFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactory.java
new file mode 100644
index 0000000..3bb6add
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactory.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.common.logger;
+
+import java.io.File;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.jcl.JclLoggerAdapter;
+import com.alibaba.dubbo.common.logger.jdk.JdkLoggerAdapter;
+import com.alibaba.dubbo.common.logger.log4j.Log4jLoggerAdapter;
+import com.alibaba.dubbo.common.logger.slf4j.Slf4jLoggerAdapter;
+import com.alibaba.dubbo.common.logger.support.FailsafeLogger;
+
+/**
+ * 日志输出器工厂
+ *
+ * @author william.liangf
+ */
+public class LoggerFactory {
+
+ private LoggerFactory() {
+ }
+
+ private static volatile LoggerAdapter LOGGER_ADAPTER;
+
+ private static final ConcurrentMap<String, FailsafeLogger> LOGGERS = new ConcurrentHashMap<String, FailsafeLogger>();
+
+ // 查找常用的日志框架
+ static {
+ String logger = System.getProperty("dubbo.application.logger");
+ if ("slf4j".equals(logger)) {
+ setLoggerAdapter(new Slf4jLoggerAdapter());
+ } else if ("jcl".equals(logger)) {
+ setLoggerAdapter(new JclLoggerAdapter());
+ } else if ("log4j".equals(logger)) {
+ setLoggerAdapter(new Log4jLoggerAdapter());
+ } else if ("jdk".equals(logger)) {
+ setLoggerAdapter(new JdkLoggerAdapter());
+ } else {
+ try {
+ setLoggerAdapter(new Log4jLoggerAdapter());
+ } catch (Throwable e1) {
+ try {
+ setLoggerAdapter(new Slf4jLoggerAdapter());
+ } catch (Throwable e2) {
+ try {
+ setLoggerAdapter(new JclLoggerAdapter());
+ } catch (Throwable e3) {
+ setLoggerAdapter(new JdkLoggerAdapter());
+ }
+ }
+ }
+ }
+ }
+
+ public static void setLoggerAdapter(String loggerAdapter) {
+ if (loggerAdapter != null && loggerAdapter.length() > 0) {
+ setLoggerAdapter(ExtensionLoader.getExtensionLoader(LoggerAdapter.class).getExtension(loggerAdapter));
+ }
+ }
+
+ /**
+ * 设置日志输出器供给器
+ *
+ * @param loggerAdapter
+ * 日志输出器供给器
+ */
+ public static void setLoggerAdapter(LoggerAdapter loggerAdapter) {
+ if (loggerAdapter != null) {
+ Logger logger = loggerAdapter.getLogger(LoggerFactory.class.getName());
+ logger.info("using logger: " + loggerAdapter.getClass().getName());
+ LoggerFactory.LOGGER_ADAPTER = loggerAdapter;
+ for (Map.Entry<String, FailsafeLogger> entry : LOGGERS.entrySet()) {
+ entry.getValue().setLogger(LOGGER_ADAPTER.getLogger(entry.getKey()));
+ }
+ }
+ }
+
+ /**
+ * 获取日志输出器
+ *
+ * @param key
+ * 分类键
+ * @return 日志输出器, 后验条件: 不返回null.
+ */
+ public static Logger getLogger(Class<?> key) {
+ FailsafeLogger logger = LOGGERS.get(key.getName());
+ if (logger == null) {
+ LOGGERS.putIfAbsent(key.getName(), new FailsafeLogger(LOGGER_ADAPTER.getLogger(key)));
+ logger = LOGGERS.get(key.getName());
+ }
+ return logger;
+ }
+
+ /**
+ * 获取日志输出器
+ *
+ * @param key
+ * 分类键
+ * @return 日志输出器, 后验条件: 不返回null.
+ */
+ public static Logger getLogger(String key) {
+ FailsafeLogger logger = LOGGERS.get(key);
+ if (logger == null) {
+ LOGGERS.putIfAbsent(key, new FailsafeLogger(LOGGER_ADAPTER.getLogger(key)));
+ logger = LOGGERS.get(key);
+ }
+ return logger;
+ }
+
+ /**
+ * 动态设置输出日志级别
+ *
+ * @param level 日志级别
+ */
+ public static void setLevel(Level level) {
+ LOGGER_ADAPTER.setLevel(level);
+ }
+
+ /**
+ * 获取日志级别
+ *
+ * @return 日志级别
+ */
+ public static Level getLevel() {
+ return LOGGER_ADAPTER.getLevel();
+ }
+
+ /**
+ * 获取日志文件
+ *
+ * @return 日志文件
+ */
+ public static File getFile() {
+ return LOGGER_ADAPTER.getFile();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jcl/JclLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jcl/JclLogger.java
new file mode 100644
index 0000000..37f04e2
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jcl/JclLogger.java
@@ -0,0 +1,107 @@
+package com.alibaba.dubbo.common.logger.jcl;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+
+import com.alibaba.dubbo.common.logger.Logger;
+
+/**
+ * 适配CommonsLogging,依赖于commons-logging.jar
+ * <br/>
+ * 有关CommonsLogging详细信息请参阅:<a target="_blank" href="http://www.apache.org/">http://www.apache.org/</a>
+ *
+ * @author liangfei0201@163.com
+ *
+ */
+public class JclLogger implements Logger, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Log logger;
+
+ public JclLogger(Log logger) {
+ this.logger = logger;
+ }
+
+ public void trace(String msg) {
+ logger.trace(msg);
+ }
+
+ public void trace(Throwable e) {
+ logger.trace(e);
+ }
+
+ public void trace(String msg, Throwable e) {
+ logger.trace(msg, e);
+ }
+
+ public void debug(String msg) {
+ logger.debug(msg);
+ }
+
+ public void debug(Throwable e) {
+ logger.debug(e);
+ }
+
+ public void debug(String msg, Throwable e) {
+ logger.debug(msg, e);
+ }
+
+ public void info(String msg) {
+ logger.info(msg);
+ }
+
+ public void info(Throwable e) {
+ logger.info(e);
+ }
+
+ public void info(String msg, Throwable e) {
+ logger.info(msg, e);
+ }
+
+ public void warn(String msg) {
+ logger.warn(msg);
+ }
+
+ public void warn(Throwable e) {
+ logger.warn(e);
+ }
+
+ public void warn(String msg, Throwable e) {
+ logger.warn(msg, e);
+ }
+
+ public void error(String msg) {
+ logger.error(msg);
+ }
+
+ public void error(Throwable e) {
+ logger.error(e);
+ }
+
+ public void error(String msg, Throwable e) {
+ logger.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.isWarnEnabled();
+ }
+
+ public boolean isErrorEnabled() {
+ return logger.isErrorEnabled();
+ }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jcl/JclLoggerAdapter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jcl/JclLoggerAdapter.java
new file mode 100644
index 0000000..ff8c633
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jcl/JclLoggerAdapter.java
@@ -0,0 +1,41 @@
+package com.alibaba.dubbo.common.logger.jcl;
+
+import java.io.File;
+
+import org.apache.commons.logging.LogFactory;
+
+import com.alibaba.dubbo.common.logger.Level;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerAdapter;
+
+public class JclLoggerAdapter implements LoggerAdapter {
+
+ public Logger getLogger(String key) {
+ return new JclLogger(LogFactory.getLog(key));
+ }
+
+ public Logger getLogger(Class<?> key) {
+ return new JclLogger(LogFactory.getLog(key));
+ }
+
+ private Level level;
+
+ private File file;
+
+ public void setLevel(Level level) {
+ this.level = level;
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jdk/JdkLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jdk/JdkLogger.java
new file mode 100644
index 0000000..f3ce929
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jdk/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.jdk;
+
+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/jdk/JdkLoggerAdapter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jdk/JdkLoggerAdapter.java
new file mode 100644
index 0000000..e3c21ed
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/jdk/JdkLoggerAdapter.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.jdk;
+
+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.LoggerAdapter;
+
+public class JdkLoggerAdapter implements LoggerAdapter {
+
+ private static final String GLOBAL_LOGGER_NAME = "global";
+
+ private File file;
+
+ public JdkLoggerAdapter() {
+ 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/log4j/Log4jLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/log4j/Log4jLogger.java
new file mode 100644
index 0000000..006d402
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/log4j/Log4jLogger.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.log4j;
+
+import org.apache.log4j.Level;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.support.FailsafeLogger;
+
+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/log4j/Log4jLoggerAdapter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/log4j/Log4jLoggerAdapter.java
new file mode 100644
index 0000000..57f7488
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/log4j/Log4jLoggerAdapter.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.log4j;
+
+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.LoggerAdapter;
+
+public class Log4jLoggerAdapter implements LoggerAdapter {
+
+ private File file;
+
+ @SuppressWarnings("unchecked")
+ public Log4jLoggerAdapter() {
+ 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/logger/slf4j/Slf4jLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/slf4j/Slf4jLogger.java
new file mode 100644
index 0000000..91db81f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/slf4j/Slf4jLogger.java
@@ -0,0 +1,97 @@
+package com.alibaba.dubbo.common.logger.slf4j;
+
+import java.io.Serializable;
+
+import com.alibaba.dubbo.common.logger.Logger;
+
+public class Slf4jLogger implements Logger, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final org.slf4j.Logger logger;
+
+ public Slf4jLogger(org.slf4j.Logger logger) {
+ this.logger = logger;
+ }
+
+ public void trace(String msg) {
+ logger.trace(msg);
+ }
+
+ public void trace(Throwable e) {
+ logger.trace(e.getMessage(), e);
+ }
+
+ public void trace(String msg, Throwable e) {
+ logger.trace(msg, e);
+ }
+
+ public void debug(String msg) {
+ logger.debug(msg);
+ }
+
+ public void debug(Throwable e) {
+ logger.debug(e.getMessage(), e);
+ }
+
+ public void debug(String msg, Throwable e) {
+ logger.debug(msg, e);
+ }
+
+ public void info(String msg) {
+ logger.info(msg);
+ }
+
+ public void info(Throwable e) {
+ logger.info(e.getMessage(), e);
+ }
+
+ public void info(String msg, Throwable e) {
+ logger.info(msg, e);
+ }
+
+ public void warn(String msg) {
+ logger.warn(msg);
+ }
+
+ public void warn(Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+
+ public void warn(String msg, Throwable e) {
+ logger.warn(msg, e);
+ }
+
+ public void error(String msg) {
+ logger.error(msg);
+ }
+
+ public void error(Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+
+ public void error(String msg, Throwable e) {
+ logger.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.isWarnEnabled();
+ }
+
+ public boolean isErrorEnabled() {
+ return logger.isErrorEnabled();
+ }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/slf4j/Slf4jLoggerAdapter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/slf4j/Slf4jLoggerAdapter.java
new file mode 100644
index 0000000..d9432ad
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/slf4j/Slf4jLoggerAdapter.java
@@ -0,0 +1,39 @@
+package com.alibaba.dubbo.common.logger.slf4j;
+
+import java.io.File;
+
+import com.alibaba.dubbo.common.logger.Level;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerAdapter;
+
+public class Slf4jLoggerAdapter implements LoggerAdapter {
+
+ public Logger getLogger(String key) {
+ return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(key));
+ }
+
+ public Logger getLogger(Class<?> key) {
+ return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(key));
+ }
+
+ private Level level;
+
+ private File file;
+
+ public void setLevel(Level level) {
+ this.level = level;
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public void setFile(File file) {
+ this.file = 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..32321cb
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/FailsafeLogger.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 Logger logger;
+
+ public FailsafeLogger(Logger logger) {
+ this.logger = logger;
+ }
+
+ public Logger getLogger() {
+ return logger;
+ }
+
+ public void setLogger(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/serialize/DataInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataInput.java
new file mode 100644
index 0000000..68d6a26
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataInput.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize;
+
+import java.io.IOException;
+
+/**
+ * Data input.
+ *
+ * @author qian.lei
+ */
+public interface DataInput {
+
+ /**
+ * Read boolean.
+ *
+ * @return boolean.
+ * @throws IOException.
+ */
+ boolean readBool() throws IOException;
+
+ /**
+ * Read byte.
+ *
+ * @return byte value.
+ * @throws IOException.
+ */
+ byte readByte() throws IOException;
+
+ /**
+ * Read short integer.
+ *
+ * @return short.
+ * @throws IOException.
+ */
+ short readShort() throws IOException;
+
+ /**
+ * Read integer.
+ *
+ * @return integer.
+ * @throws IOException.
+ */
+ int readInt() throws IOException;
+
+ /**
+ * Read long.
+ *
+ * @return long.
+ * @throws IOException.
+ */
+ long readLong() throws IOException;
+
+ /**
+ * Read float.
+ *
+ * @return float.
+ * @throws IOException.
+ */
+ float readFloat() throws IOException;
+
+ /**
+ * Read double.
+ *
+ * @return double.
+ * @throws IOException.
+ */
+ double readDouble() throws IOException;
+
+ /**
+ * Read UTF-8 string.
+ *
+ * @return string.
+ * @throws IOException.
+ */
+ String readUTF() throws IOException;
+
+ /**
+ * Read byte array.
+ *
+ * @return byte array.
+ * @throws IOException.
+ */
+ byte[] readBytes() throws IOException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataOutput.java
new file mode 100644
index 0000000..8e5970e
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataOutput.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize;
+
+import java.io.IOException;
+
+/**
+ * Data output.
+ *
+ * @author qian.lei
+ */
+public interface DataOutput {
+
+ /**
+ * Write boolean.
+ *
+ * @param v value.
+ * @throws IOException.
+ */
+ void writeBool(boolean v) throws IOException;
+
+ /**
+ * Write byte.
+ *
+ * @param v value.
+ * @throws IOException.
+ */
+ void writeByte(byte v) throws IOException;
+
+ /**
+ * Write short.
+ *
+ * @param v value.
+ * @throws IOException.
+ */
+ void writeShort(short v) throws IOException;
+
+ /**
+ * Write integer.
+ *
+ * @param v value.
+ * @throws IOException.
+ */
+ void writeInt(int v) throws IOException;
+
+ /**
+ * Write long.
+ *
+ * @param v value.
+ * @throws IOException.
+ */
+ void writeLong(long v) throws IOException;
+
+ /**
+ * Write float.
+ *
+ * @param v value.
+ * @throws IOException.
+ */
+ void writeFloat(float v) throws IOException;
+
+ /**
+ * Write double.
+ *
+ * @param v value.
+ * @throws IOException.
+ */
+ void writeDouble(double v) throws IOException;
+
+ /**
+ * Write string.
+ *
+ * @param v value.
+ * @throws IOException.
+ */
+ void writeUTF(String v) throws IOException;
+
+ /**
+ * Write byte array.
+ *
+ * @param v value.
+ * @throws IOException.
+ */
+ void writeBytes(byte[] v) throws IOException;
+
+ /**
+ * Write byte array.
+ *
+ * @param v value.
+ * @param off offset.
+ * @param len length.
+ * @throws IOException.
+ */
+ void writeBytes(byte[] v, int off, int len) throws IOException;
+
+ /**
+ * Flush buffer.
+ *
+ * @throws IOException.
+ */
+ void flushBuffer() throws IOException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectInput.java
new file mode 100644
index 0000000..67a4651
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectInput.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+/**
+ * Object input.
+ *
+ * @author qian.lei
+ */
+public interface ObjectInput extends DataInput {
+
+ /**
+ * read object.
+ *
+ * @return object.
+ */
+ Object readObject() throws IOException, ClassNotFoundException;
+
+ /**
+ * read object.
+ *
+ * @param cls object type.
+ * @return object.
+ */
+ <T> T readObject(Class<T> cls) throws IOException, ClassNotFoundException;
+
+ /**
+ * read object.
+ *
+ * @param cls object type.
+ * @return object.
+ */
+ <T> T readObject(Class<T> cls, Type type) throws IOException, ClassNotFoundException;
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectOutput.java
new file mode 100644
index 0000000..c91d416
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectOutput.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize;
+
+import java.io.IOException;
+
+/**
+ * Object output.
+ *
+ * @author qian.lei
+ */
+public interface ObjectOutput extends DataOutput {
+
+ /**
+ * write object.
+ *
+ * @param obj object.
+ */
+ void writeObject(Object obj) throws IOException;
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Serialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Serialization.java
new file mode 100644
index 0000000..8c8f1d0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Serialization.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * Serialization. (SPI, Singleton, ThreadSafe)
+ *
+ * @author ding.lid
+ * @author william.liangf
+ */
+@SPI("hessian2")
+public interface Serialization {
+
+ /**
+ * get content type id
+ *
+ * @return content type id
+ */
+ byte getContentTypeId();
+
+ /**
+ * get content type
+ *
+ * @return content type
+ */
+ String getContentType();
+
+ /**
+ * create serializer
+ * @param url
+ * @param output
+ * @return serializer
+ * @throws IOException
+ */
+ @Adaptive
+ ObjectOutput serialize(URL url, OutputStream output) throws IOException;
+
+ /**
+ * create deserializer
+ * @param url
+ * @param input
+ * @return deserializer
+ * @throws IOException
+ */
+ @Adaptive
+ ObjectInput deserialize(URL url, InputStream input) throws IOException;
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/Builder.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/Builder.java
new file mode 100644
index 0000000..0ffd6f0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/Builder.java
@@ -0,0 +1,1569 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Matcher;
+
+import com.alibaba.dubbo.common.bytecode.ClassGenerator;
+import com.alibaba.dubbo.common.io.ClassDescriptorMapper;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectInputStream;
+import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectOutputStream;
+import com.alibaba.dubbo.common.utils.ClassHelper;
+import com.alibaba.dubbo.common.utils.IOUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+/**
+ * Builder.
+ *
+ * @author qian.lei
+ *
+ * @param <T> type.
+ */
+
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public abstract class Builder<T> implements GenericDataFlags
+{
+ // Must be protected. by qian.lei
+ protected static Logger logger = LoggerFactory.getLogger(Builder.class);
+
+ private static final AtomicLong BUILDER_CLASS_COUNTER = new AtomicLong(0);
+
+ private static final String BUILDER_CLASS_NAME = Builder.class.getName();
+
+ private static final Map<Class<?>, Builder<?>> BuilderMap = new ConcurrentHashMap<Class<?>, Builder<?>>();
+ private static final Map<Class<?>, Builder<?>> nonSerializableBuilderMap = new ConcurrentHashMap<Class<?>, Builder<?>>();
+
+ private static final String FIELD_CONFIG_SUFFIX = ".fc";
+
+ private static final int MAX_FIELD_CONFIG_FILE_SIZE = 16 * 1024;
+
+ private static final Comparator<String> FNC = new Comparator<String>(){
+ public int compare(String n1, String n2){ return compareFieldName(n1, n2); }
+ };
+
+ private static final Comparator<Field> FC = new Comparator<Field>(){
+ public int compare(Field f1, Field f2){ return compareFieldName(f1.getName(), f2.getName()); }
+ };
+
+ private static final Comparator<Constructor> CC = new Comparator<Constructor>(){
+ public int compare(Constructor o1, Constructor o2){ return o1.getParameterTypes().length - o2.getParameterTypes().length; }
+ };
+
+ // class-descriptor mapper
+ private static final List<String> mDescList = new ArrayList<String>();
+
+ private static final Map<String, Integer> mDescMap = new ConcurrentHashMap<String, Integer>();
+
+ public static ClassDescriptorMapper DEFAULT_CLASS_DESCRIPTOR_MAPPER = new ClassDescriptorMapper(){
+ public String getDescriptor(int index)
+ {
+ if( index < 0 || index >= mDescList.size() )
+ return null;
+ return mDescList.get(index);
+ }
+
+ public int getDescriptorIndex(String desc)
+ {
+ Integer ret = mDescMap.get(desc);
+ return ret == null ? -1 : ret.intValue();
+ }
+ };
+
+ protected Builder(){}
+
+ abstract public Class<T> getType();
+
+ public void writeTo(T obj, OutputStream os) throws IOException
+ {
+ GenericObjectOutput out = new GenericObjectOutput(os);
+ writeTo(obj, out);
+ out.flushBuffer();
+ }
+
+ public T parseFrom(byte[] b) throws IOException
+ {
+ return parseFrom(new UnsafeByteArrayInputStream(b));
+ }
+
+ public T parseFrom(InputStream is) throws IOException
+ {
+ return parseFrom(new GenericObjectInput(is));
+ }
+
+ abstract public void writeTo(T obj, GenericObjectOutput out) throws IOException;
+
+ abstract public T parseFrom(GenericObjectInput in) throws IOException;
+
+ public static <T> Builder<T> register(Class<T> c, boolean isAllowNonSerializable)
+ {
+ if( c == Object.class || c.isInterface() )
+ return (Builder<T>)GenericBuilder;
+ if( c == Object[].class )
+ return (Builder<T>)GenericArrayBuilder;
+
+ Builder<T> b = (Builder<T>)BuilderMap.get(c);
+ if(null != b) return b;
+
+ boolean isSerializable = Serializable.class.isAssignableFrom(c);
+ if(!isAllowNonSerializable && !isSerializable) {
+ throw new IllegalStateException("Serialized class " + c.getName() +
+ " must implement java.io.Serializable (dubbo codec setting: isAllowNonSerializable = false)");
+ }
+
+ b = (Builder<T>)nonSerializableBuilderMap.get(c);
+ if(null != b) return b;
+
+ b = newBuilder(c);
+ if(isSerializable)
+ BuilderMap.put(c, b);
+ else
+ nonSerializableBuilderMap.put(c, b);
+
+ return b;
+ }
+
+ public static <T> Builder<T> register(Class<T> c)
+ {
+ return register(c, false);
+ }
+
+ public static <T> void register(Class<T> c, Builder<T> b)
+ {
+ if(Serializable.class.isAssignableFrom(c))
+ BuilderMap.put(c, b);
+ else
+ nonSerializableBuilderMap.put(c, b);
+ }
+
+ private static <T> Builder<T> newBuilder(Class<T> c)
+ {
+ if( c.isPrimitive() )
+ throw new RuntimeException("Can not create builder for primitive type: " + c);
+
+ if( logger.isInfoEnabled() )
+ logger.info("create Builder for class: " + c);
+
+ Builder<?> builder;
+ if( c.isArray() )
+ builder = newArrayBuilder(c);
+ else
+ builder = newObjectBuilder(c);
+ return (Builder<T>)builder;
+ }
+
+ private static Builder<?> newArrayBuilder(Class<?> c)
+ {
+ Class<?> cc = c.getComponentType();
+ if( cc.isInterface() )
+ return GenericArrayBuilder;
+
+ ClassLoader cl = ClassHelper.getClassLoader(c);
+
+ String cn = ReflectUtils.getName(c), ccn = ReflectUtils.getName(cc); // get class name as int[][], double[].
+ String bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
+
+ int ix = cn.indexOf(']');
+ String s1 = cn.substring(0, ix), s2 = cn.substring(ix); // if name='int[][]' then s1='int[', s2='][]'
+
+ StringBuilder cwt = new StringBuilder("public void writeTo(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{"); // writeTo code.
+ StringBuilder cpf = new StringBuilder("public Object parseFrom(").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{"); // parseFrom code.
+
+ cwt.append("if( $1 == null ){ $2.write0(OBJECT_NULL); return; }");
+ cwt.append(cn).append(" v = (").append(cn).append(")$1; int len = v.length; $2.write0(OBJECT_VALUES); $2.writeUInt(len); for(int i=0;i<len;i++){ ");
+
+ cpf.append("byte b = $1.read0(); if( b == OBJECT_NULL ) return null; if( b != OBJECT_VALUES ) throw new java.io.IOException(\"Input format error, expect OBJECT_NULL|OBJECT_VALUES, get \" + b + \".\");");
+ cpf.append("int len = $1.readUInt(); if( len == 0 ) return new ").append(s1).append('0').append(s2).append("; ");
+ cpf.append(cn).append(" ret = new ").append(s1).append("len").append(s2).append("; for(int i=0;i<len;i++){ ");
+
+ Builder<?> builder = null;
+ if( cc.isPrimitive() )
+ {
+ if( cc == boolean.class )
+ {
+ cwt.append("$2.writeBool(v[i]);");
+ cpf.append("ret[i] = $1.readBool();");
+ }
+ else if( cc == byte.class )
+ {
+ cwt.append("$2.writeByte(v[i]);");
+ cpf.append("ret[i] = $1.readByte();");
+ }
+ else if( cc == char.class )
+ {
+ cwt.append("$2.writeShort((short)v[i]);");
+ cpf.append("ret[i] = (char)$1.readShort();");
+ }
+ else if( cc == short.class )
+ {
+ cwt.append("$2.writeShort(v[i]);");
+ cpf.append("ret[i] = $1.readShort();");
+ }
+ else if( cc == int.class )
+ {
+ cwt.append("$2.writeInt(v[i]);");
+ cpf.append("ret[i] = $1.readInt();");
+ }
+ else if( cc == long.class )
+ {
+ cwt.append("$2.writeLong(v[i]);");
+ cpf.append("ret[i] = $1.readLong();");
+ }
+ else if( cc == float.class )
+ {
+ cwt.append("$2.writeFloat(v[i]);");
+ cpf.append("ret[i] = $1.readFloat();");
+ }
+ else if( cc == double.class )
+ {
+ cwt.append("$2.writeDouble(v[i]);");
+ cpf.append("ret[i] = $1.readDouble();");
+ }
+ }
+ else
+ {
+ builder = register(cc);
+
+ cwt.append("builder.writeTo(v[i], $2);");
+ cpf.append("ret[i] = (").append(ccn).append(")builder.parseFrom($1);");
+ }
+ cwt.append(" } }");
+ cpf.append(" } return ret; }");
+
+ ClassGenerator cg = ClassGenerator.newInstance(cl);
+ cg.setClassName(bcn);
+ cg.setSuperClass(Builder.class);
+ cg.addDefaultConstructor();
+ if( builder != null )
+ cg.addField("public static " + BUILDER_CLASS_NAME + " builder;");
+ cg.addMethod("public Class getType(){ return " + cn + ".class; }");
+ cg.addMethod(cwt.toString());
+ cg.addMethod(cpf.toString());
+ try
+ {
+ Class<?> wc = cg.toClass();
+ // set static field.
+ if( builder != null )
+ wc.getField("builder").set(null, builder);
+ return (Builder<?>)wc.newInstance();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(Throwable e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+ finally
+ {
+ cg.release();
+ }
+ }
+
+ private static Builder<?> newObjectBuilder(final Class<?> c)
+ {
+ if( c.isEnum() )
+ return newEnumBuilder(c);
+
+ if( c.isAnonymousClass() )
+ throw new RuntimeException("Can not instantiation anonymous class: " + c);
+
+ if( c.getEnclosingClass() != null && !Modifier.isStatic(c.getModifiers()) )
+ throw new RuntimeException("Can not instantiation inner and non-static class: " + c);
+
+ if( Throwable.class.isAssignableFrom(c) )
+ return SerializableBuilder;
+
+ ClassLoader cl = ClassHelper.getClassLoader(c);
+
+ // is same package.
+ boolean isp;
+ String cn = c.getName(), bcn;
+ if( c.getClassLoader() == null ) // is system class. if( cn.startsWith("java.") || cn.startsWith("javax.") || cn.startsWith("sun.") )
+ {
+ isp = false;
+ bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
+ }
+ else
+ {
+ isp = true;
+ bcn = cn + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
+ }
+
+ // is Collection, is Map, is Serializable.
+ boolean isc = Collection.class.isAssignableFrom(c);
+ boolean ism = !isc && Map.class.isAssignableFrom(c);
+ boolean iss = !( isc || ism ) && Serializable.class.isAssignableFrom(c);
+
+ // deal with fields.
+ String[] fns = null; // fix-order fields names
+ InputStream is = c.getResourceAsStream(c.getSimpleName() + FIELD_CONFIG_SUFFIX); // load field-config file.
+ if( is != null )
+ {
+ try
+ {
+ int len = is.available();
+ if( len > 0 )
+ {
+ if( len > MAX_FIELD_CONFIG_FILE_SIZE )
+ throw new RuntimeException("Load [" + c.getName() + "] field-config file error: File-size too larger");
+
+ String[] lines = IOUtils.readLines(is);
+ if( lines != null && lines.length > 0 )
+ {
+ List<String> list = new ArrayList<String>();
+ for(int i=0;i<lines.length;i++)
+ {
+ fns = lines[i].split(",");
+ Arrays.sort(fns, FNC);
+ for(int j=0;j<fns.length;j++)
+ list.add(fns[j]);
+ }
+ fns = list.toArray(new String[0]);
+ }
+ }
+ }
+ catch(IOException e)
+ {
+ throw new RuntimeException("Load [" + c.getName() + "] field-config file error: " + e.getMessage() );
+ }
+ finally
+ {
+ try{ is.close(); }
+ catch(IOException e){}
+ }
+ }
+
+ Field f, fs[];
+ if( fns != null )
+ {
+ fs = new Field[fns.length];
+ for(int i=0;i<fns.length;i++)
+ {
+ String fn = fns[i];
+ try
+ {
+ f = c.getDeclaredField(fn);
+ int mod = f.getModifiers();
+ if( Modifier.isStatic(mod) || (serializeIgnoreFinalModifier(c) && Modifier.isFinal(mod)) )
+ throw new RuntimeException("Field [" + c.getName() + "." + fn + "] is static/final field.");
+ if( Modifier.isTransient(mod) )
+ {
+ if( iss )
+ return SerializableBuilder;
+ throw new RuntimeException("Field [" + c.getName() + "." + fn + "] is transient field.");
+ }
+ f.setAccessible(true);
+ fs[i] = f;
+ }
+ catch(SecurityException e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+ catch(NoSuchFieldException e)
+ {
+ throw new RuntimeException("Field [" + c.getName() + "." + fn + "] not found.");
+ }
+ }
+ }
+ else
+ {
+ Class<?> t = c;
+ List<Field> fl = new ArrayList<Field>();
+ do
+ {
+ fs = t.getDeclaredFields();
+ for( Field tf : fs )
+ {
+ int mod = tf.getModifiers();
+ if (Modifier.isStatic(mod)
+ || (serializeIgnoreFinalModifier(c) && Modifier.isFinal(mod))
+ || tf.getName().equals("this$0") // skip static or inner-class's 'this$0' field.
+ || ! Modifier.isPublic(tf.getType().getModifiers()) ) //skip private inner-class field
+ continue;
+ if( Modifier.isTransient(mod) )
+ {
+ if( iss )
+ return SerializableBuilder;
+ continue;
+ }
+ tf.setAccessible(true);
+ fl.add(tf);
+ }
+ t = t.getSuperclass();
+ }
+ while( t != Object.class );
+
+ fs = fl.toArray(new Field[0]);
+ if( fs.length > 1 )
+ Arrays.sort(fs, FC);
+ }
+
+ // deal with constructors.
+ Constructor<?>[] cs = c.getDeclaredConstructors();
+ if( cs.length == 0 )
+ {
+ Class<?> t = c;
+ do
+ {
+ t = t.getSuperclass();
+ if( t == null )
+ throw new RuntimeException("Can not found Constructor?");
+ cs = c.getDeclaredConstructors();
+ }
+ while( cs.length == 0 );
+ }
+ if( cs.length > 1 )
+ Arrays.sort(cs, CC);
+
+ // writeObject code.
+ StringBuilder cwf = new StringBuilder("protected void writeObject(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{");
+ cwf.append(cn).append(" v = (").append(cn).append(")$1; ");
+ cwf.append("$2.writeInt(fields.length);");
+
+ // readObject code.
+ StringBuilder crf = new StringBuilder("protected void readObject(Object ret, ").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{");
+ crf.append("int fc = $2.readInt();");
+ crf.append("if( fc != ").append(fs.length).append(" ) throw new IllegalStateException(\"Deserialize Class [").append(cn).append("], field count not matched. Expect ").append(fs.length).append(" but get \" + fc +\".\");");
+ crf.append(cn).append(" ret = (").append(cn).append(")$1;");
+
+ // newInstance code.
+ StringBuilder cni = new StringBuilder("protected Object newInstance(").append(GenericObjectInput.class.getName()).append(" in){ return ");
+ Constructor<?> con = cs[0];
+ int mod = con.getModifiers();
+ boolean dn = Modifier.isPublic(mod) || ( isp && !Modifier.isPrivate(mod) );
+ if( dn )
+ {
+ cni.append("new ").append(cn).append("(");
+ }
+ else
+ {
+ con.setAccessible(true);
+ cni.append("constructor.newInstance(new Object[]{");
+ }
+ Class<?>[] pts = con.getParameterTypes();
+ for(int i=0;i<pts.length;i++)
+ {
+ if( i > 0 )
+ cni.append(',');
+ cni.append(defaultArg(pts[i]));
+ }
+ if( !dn )
+ cni.append("}"); // close object array.
+ cni.append("); }");
+
+ // get bean-style property metadata.
+ Map<String, PropertyMetadata> pms = propertyMetadatas(c);
+ List<Builder<?>> builders = new ArrayList<Builder<?>>(fs.length);
+ String fn, ftn; // field name, field type name.
+ Class<?> ft; // field type.
+ boolean da; // direct access.
+ PropertyMetadata pm;
+ for(int i=0;i<fs.length;i++)
+ {
+ f = fs[i];
+ fn = f.getName();
+ ft = f.getType();
+ ftn = ReflectUtils.getName(ft);
+ da = isp && ( f.getDeclaringClass() == c ) && ( Modifier.isPrivate(f.getModifiers()) == false );
+ if( da )
+ {
+ pm = null;
+ }
+ else
+ {
+ pm = pms.get(fn);
+ if( pm != null && ( pm.type != ft || pm.setter == null || pm.getter == null ) )
+ pm = null;
+ }
+
+ crf.append("if( fc == ").append(i).append(" ) return;");
+ if( ft.isPrimitive() )
+ {
+ if( ft == boolean.class )
+ {
+ if( da )
+ {
+ cwf.append("$2.writeBool(v.").append(fn).append(");");
+ crf.append("ret.").append(fn).append(" = $2.readBool();");
+ }
+ else if( pm != null )
+ {
+ cwf.append("$2.writeBool(v.").append(pm.getter).append("());");
+ crf.append("ret.").append(pm.setter).append("($2.readBool());");
+ }
+ else
+ {
+ cwf.append("$2.writeBool(((Boolean)fields[").append(i).append("].get($1)).booleanValue());");
+ crf.append("fields[").append(i).append("].set(ret, ($w)$2.readBool());");
+ }
+ }
+ else if( ft == byte.class )
+ {
+ if( da )
+ {
+ cwf.append("$2.writeByte(v.").append(fn).append(");");
+ crf.append("ret.").append(fn).append(" = $2.readByte();");
+ }
+ else if( pm != null )
+ {
+ cwf.append("$2.writeByte(v.").append(pm.getter).append("());");
+ crf.append("ret.").append(pm.setter).append("($2.readByte());");
+ }
+ else
+ {
+ cwf.append("$2.writeByte(((Byte)fields[").append(i).append("].get($1)).byteValue());");
+ crf.append("fields[").append(i).append("].set(ret, ($w)$2.readByte());");
+ }
+ }
+ else if( ft == char.class )
+ {
+ if( da )
+ {
+ cwf.append("$2.writeShort((short)v.").append(fn).append(");");
+ crf.append("ret.").append(fn).append(" = (char)$2.readShort();");
+ }
+ else if( pm != null )
+ {
+ cwf.append("$2.writeShort((short)v.").append(pm.getter).append("());");
+ crf.append("ret.").append(pm.setter).append("((char)$2.readShort());");
+ }
+ else
+ {
+ cwf.append("$2.writeShort((short)((Character)fields[").append(i).append("].get($1)).charValue());");
+ crf.append("fields[").append(i).append("].set(ret, ($w)((char)$2.readShort()));");
+ }
+ }
+ else if( ft == short.class )
+ {
+ if( da )
+ {
+ cwf.append("$2.writeShort(v.").append(fn).append(");");
+ crf.append("ret.").append(fn).append(" = $2.readShort();");
+ }
+ else if( pm != null )
+ {
+ cwf.append("$2.writeShort(v.").append(pm.getter).append("());");
+ crf.append("ret.").append(pm.setter).append("($2.readShort());");
+ }
+ else
+ {
+ cwf.append("$2.writeShort(((Short)fields[").append(i).append("].get($1)).shortValue());");
+ crf.append("fields[").append(i).append("].set(ret, ($w)$2.readShort());");
+ }
+ }
+ else if( ft == int.class )
+ {
+ if( da )
+ {
+ cwf.append("$2.writeInt(v.").append(fn).append(");");
+ crf.append("ret.").append(fn).append(" = $2.readInt();");
+ }
+ else if( pm != null )
+ {
+ cwf.append("$2.writeInt(v.").append(pm.getter).append("());");
+ crf.append("ret.").append(pm.setter).append("($2.readInt());");
+ }
+ else
+ {
+ cwf.append("$2.writeInt(((Integer)fields[").append(i).append("].get($1)).intValue());");
+ crf.append("fields[").append(i).append("].set(ret, ($w)$2.readInt());");
+ }
+ }
+ else if( ft == long.class )
+ {
+ if( da )
+ {
+ cwf.append("$2.writeLong(v.").append(fn).append(");");
+ crf.append("ret.").append(fn).append(" = $2.readLong();");
+ }
+ else if( pm != null )
+ {
+ cwf.append("$2.writeLong(v.").append(pm.getter).append("());");
+ crf.append("ret.").append(pm.setter).append("($2.readLong());");
+ }
+ else
+ {
+ cwf.append("$2.writeLong(((Long)fields[").append(i).append("].get($1)).longValue());");
+ crf.append("fields[").append(i).append("].set(ret, ($w)$2.readLong());");
+ }
+ }
+ else if( ft == float.class )
+ {
+ if( da )
+ {
+ cwf.append("$2.writeFloat(v.").append(fn).append(");");
+ crf.append("ret.").append(fn).append(" = $2.readFloat();");
+ }
+ else if( pm != null )
+ {
+ cwf.append("$2.writeFloat(v.").append(pm.getter).append("());");
+ crf.append("ret.").append(pm.setter).append("($2.readFloat());");
+ }
+ else
+ {
+ cwf.append("$2.writeFloat(((Float)fields[").append(i).append("].get($1)).floatValue());");
+ crf.append("fields[").append(i).append("].set(ret, ($w)$2.readFloat());");
+ }
+ }
+ else if( ft == double.class )
+ {
+ if( da )
+ {
+ cwf.append("$2.writeDouble(v.").append(fn).append(");");
+ crf.append("ret.").append(fn).append(" = $2.readDouble();");
+ }
+ else if( pm != null )
+ {
+ cwf.append("$2.writeDouble(v.").append(pm.getter).append("());");
+ crf.append("ret.").append(pm.setter).append("($2.readDouble());");
+ }
+ else
+ {
+ cwf.append("$2.writeDouble(((Double)fields[").append(i).append("].get($1)).doubleValue());");
+ crf.append("fields[").append(i).append("].set(ret, ($w)$2.readDouble());");
+ }
+ }
+ }
+ else if( ft == c )
+ {
+ if( da )
+ {
+ cwf.append("this.writeTo(v.").append(fn).append(", $2);");
+ crf.append("ret.").append(fn).append(" = (").append(ftn).append(")this.parseFrom($2);");
+ }
+ else if( pm != null )
+ {
+ cwf.append("this.writeTo(v.").append(pm.getter).append("(), $2);");
+ crf.append("ret.").append(pm.setter).append("((").append(ftn).append(")this.parseFrom($2));");
+ }
+ else
+ {
+ cwf.append("this.writeTo((").append(ftn).append(")fields[").append(i).append("].get($1), $2);");
+ crf.append("fields[").append(i).append("].set(ret, this.parseFrom($2));");
+ }
+ }
+ else
+ {
+ int bc = builders.size();
+ builders.add( register(ft) );
+
+ if( da )
+ {
+ cwf.append("builders[").append(bc).append("].writeTo(v.").append(fn).append(", $2);");
+ crf.append("ret.").append(fn).append(" = (").append(ftn).append(")builders[").append(bc).append("].parseFrom($2);");
+ }
+ else if( pm != null )
+ {
+ cwf.append("builders[").append(bc).append("].writeTo(v.").append(pm.getter).append("(), $2);");
+ crf.append("ret.").append(pm.setter).append("((").append(ftn).append(")builders[").append(bc).append("].parseFrom($2));");
+ }
+ else
+ {
+ cwf.append("builders[").append(bc).append("].writeTo((").append(ftn).append(")fields[").append(i).append("].get($1), $2);");
+ crf.append("fields[").append(i).append("].set(ret, builders[").append(bc).append("].parseFrom($2));");
+ }
+ }
+ }
+
+ // skip any fields.
+ crf.append("for(int i=").append(fs.length).append(";i<fc;i++) $2.skipAny();");
+
+ // collection or map
+ if( isc )
+ {
+ cwf.append("$2.writeInt(v.size()); for(java.util.Iterator it=v.iterator();it.hasNext();){ $2.writeObject(it.next()); }");
+ crf.append("int len = $2.readInt(); for(int i=0;i<len;i++) ret.add($2.readObject());");
+ }
+ else if( ism )
+ {
+ cwf.append("$2.writeInt(v.size()); for(java.util.Iterator it=v.entrySet().iterator();it.hasNext();){ java.util.Map.Entry entry = (java.util.Map.Entry)it.next(); $2.writeObject(entry.getKey()); $2.writeObject(entry.getValue()); }");
+ crf.append("int len = $2.readInt(); for(int i=0;i<len;i++) ret.put($2.readObject(), $2.readObject());");
+ }
+ cwf.append(" }");
+ crf.append(" }");
+
+ ClassGenerator cg = ClassGenerator.newInstance(cl);
+ cg.setClassName(bcn);
+ cg.setSuperClass(AbstractObjectBuilder.class);
+ cg.addDefaultConstructor();
+ cg.addField("public static java.lang.reflect.Field[] fields;");
+ cg.addField("public static " + BUILDER_CLASS_NAME + "[] builders;");
+ if( !dn )
+ cg.addField("public static java.lang.reflect.Constructor constructor;");
+ cg.addMethod("public Class getType(){ return " + cn + ".class; }");
+ cg.addMethod(cwf.toString());
+ cg.addMethod(crf.toString());
+ cg.addMethod(cni.toString());
+ try
+ {
+ Class<?> wc = cg.toClass();
+ // set static field
+ wc.getField("fields").set(null, fs);
+ wc.getField("builders").set(null, builders.toArray(new Builder<?>[0]));
+ if( !dn )
+ wc.getField("constructor").set(null, con);
+ return (Builder<?>)wc.newInstance();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(Throwable e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ finally
+ {
+ cg.release();
+ }
+ }
+
+ private static Builder<?> newEnumBuilder(Class<?> c)
+ {
+ ClassLoader cl = ClassHelper.getClassLoader(c);
+
+ String cn = c.getName();
+ String bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
+
+ StringBuilder cwt = new StringBuilder("public void writeTo(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{"); // writeTo code.
+ cwt.append(cn).append(" v = (").append(cn).append(")$1;");
+ cwt.append("if( $1 == null ){ $2.writeUTF(null); }else{ $2.writeUTF(v.name()); } }");
+
+ StringBuilder cpf = new StringBuilder("public Object parseFrom(").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{"); // parseFrom code.
+ cpf.append("String name = $1.readUTF(); if( name == null ) return null; return (").append(cn).append(")Enum.valueOf(").append(cn).append(".class, name); }");
+
+ ClassGenerator cg = ClassGenerator.newInstance(cl);
+ cg.setClassName(bcn);
+ cg.setSuperClass(Builder.class);
+ cg.addDefaultConstructor();
+ cg.addMethod("public Class getType(){ return " + cn + ".class; }");
+ cg.addMethod(cwt.toString());
+ cg.addMethod(cpf.toString());
+ try
+ {
+ Class<?> wc = cg.toClass();
+ return (Builder<?>)wc.newInstance();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(Throwable e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ finally
+ {
+ cg.release();
+ }
+ }
+
+ private static Map<String, PropertyMetadata> propertyMetadatas(Class<?> c)
+ {
+ Map<String, Method> mm = new HashMap<String, Method>(); // method map.
+ Map<String, PropertyMetadata> ret = new HashMap<String, PropertyMetadata>(); // property metadata map.
+
+ // All public method.
+ for( Method m : c.getMethods() )
+ {
+ if( m.getDeclaringClass() == Object.class ) // Ignore Object's method.
+ continue;
+ mm.put(ReflectUtils.getDesc(m), m);
+ }
+
+ Matcher matcher;
+ for( Map.Entry<String,Method> entry : mm.entrySet() )
+ {
+ String desc = entry.getKey();
+ Method method = entry.getValue();
+ if( ( matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(desc) ).matches() ||
+ ( matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(desc) ).matches() )
+ {
+ String pn = propertyName(matcher.group(1));
+ Class<?> pt = method.getReturnType();
+ PropertyMetadata pm = ret.get(pn);
+ if( pm == null )
+ {
+ pm = new PropertyMetadata();
+ pm.type = pt;
+ ret.put(pn, pm);
+ }
+ else
+ {
+ if( pm.type != pt )
+ continue;
+ }
+ pm.getter = method.getName();
+ }
+ else if( ( matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(desc) ).matches() )
+ {
+ String pn = propertyName(matcher.group(1));
+ Class<?> pt = method.getParameterTypes()[0];
+ PropertyMetadata pm = ret.get(pn);
+ if( pm == null )
+ {
+ pm = new PropertyMetadata();
+ pm.type = pt;
+ ret.put(pn, pm);
+ }
+ else
+ {
+ if( pm.type != pt )
+ continue;
+ }
+ pm.setter = method.getName();
+ }
+ }
+ return ret;
+ }
+
+ private static String propertyName(String s)
+ {
+ return s.length() == 1 || Character.isLowerCase(s.charAt(1)) ? Character.toLowerCase(s.charAt(0)) + s.substring(1) : s;
+ }
+
+ private static boolean serializeIgnoreFinalModifier(Class cl)
+ {
+// if (cl.isAssignableFrom(BigInteger.class)) return false;
+// for performance
+// if (cl.getName().startsWith("java")) return true;
+// if (cl.getName().startsWith("javax")) return true;
+
+ return false;
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean isPrimitiveOrPrimitiveArray1(Class<?> cl)
+ {
+ if (cl.isPrimitive()){
+ return true;
+ } else {
+ Class clazz = cl.getClass().getComponentType();
+ if (clazz!=null && clazz.isPrimitive()){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String defaultArg(Class<?> cl)
+ {
+ if( boolean.class == cl ) return "false";
+ if( int.class == cl ) return "0";
+ if( long.class == cl ) return "0l";
+ if( double.class == cl ) return "(double)0";
+ if( float.class == cl ) return "(float)0";
+ if( short.class == cl ) return "(short)0";
+ if( char.class == cl ) return "(char)0";
+ if( byte.class == cl ) return "(byte)0";
+ if( byte[].class == cl ) return "new byte[]{0}";
+ if( !cl.isPrimitive() ) return "null";
+ throw new UnsupportedOperationException();
+ }
+
+ private static int compareFieldName(String n1, String n2)
+ {
+ int l = Math.min(n1.length(), n2.length());
+ for(int i=0;i<l;i++)
+ {
+ int t = n1.charAt(i) - n2.charAt(i);
+ if( t != 0 )
+ return t;
+ }
+ return n1.length() - n2.length();
+ }
+
+ private static void addDesc(Class<?> c)
+ {
+ String desc = ReflectUtils.getDesc(c);
+ int index = mDescList.size();
+ mDescList.add(desc);
+ mDescMap.put(desc, index);
+ }
+
+ static class PropertyMetadata
+ {
+ Class<?> type;
+ String setter, getter;
+ }
+
+ public static abstract class AbstractObjectBuilder<T> extends Builder<T>
+ {
+ abstract public Class<T> getType();
+
+ public void writeTo(T obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ int ref = out.getRef(obj);
+ if( ref < 0 )
+ {
+ out.addRef(obj);
+ out.write0(OBJECT);
+ writeObject(obj, out);
+ }
+ else
+ {
+ out.write0(OBJECT_REF);
+ out.writeUInt(ref);
+ }
+ }
+ }
+
+ public T parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ switch( b )
+ {
+ case OBJECT:
+ {
+ T ret = newInstance(in);
+ in.addRef(ret);
+ readObject(ret, in);
+ return ret;
+ }
+ case OBJECT_REF:
+ return (T)in.getRef(in.readUInt());
+ case OBJECT_NULL:
+ return null;
+ default:
+ throw new IOException("Input format error, expect OBJECT|OBJECT_REF|OBJECT_NULL, get " + b);
+ }
+ }
+
+ abstract protected void writeObject(T obj, GenericObjectOutput out) throws IOException;
+
+ abstract protected T newInstance(GenericObjectInput in) throws IOException;
+
+ abstract protected void readObject(T ret, GenericObjectInput in) throws IOException;
+ }
+
+ static final Builder<Object> GenericBuilder = new Builder<Object>(){
+ @Override
+ public Class<Object> getType(){ return Object.class; }
+ @Override
+ public void writeTo(Object obj, GenericObjectOutput out) throws IOException{ out.writeObject(obj); }
+ @Override
+ public Object parseFrom(GenericObjectInput in) throws IOException{ return in.readObject(); }
+ };
+
+ static final Builder<Object[]> GenericArrayBuilder = new AbstractObjectBuilder<Object[]>(){
+ @Override
+ public Class<Object[]> getType(){ return Object[].class; }
+ @Override
+ protected Object[] newInstance(GenericObjectInput in) throws IOException
+ {
+ return new Object[in.readUInt()];
+ }
+ @Override
+ protected void readObject(Object[] ret, GenericObjectInput in) throws IOException
+ {
+ for(int i=0;i<ret.length;i++)
+ ret[i] = in.readObject();
+ }
+ @Override
+ protected void writeObject(Object[] obj, GenericObjectOutput out) throws IOException
+ {
+ out.writeUInt(obj.length);
+ for( Object item : obj )
+ out.writeObject(item);
+ }
+ };
+
+ static final Builder<Serializable> SerializableBuilder = new Builder<Serializable>(){
+ @Override
+ public Class<Serializable> getType()
+ {
+ return Serializable.class;
+ }
+ @Override
+ public void writeTo(Serializable obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_STREAM);
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
+ CompactedObjectOutputStream oos = new CompactedObjectOutputStream(bos);
+ oos.writeObject(obj);
+ oos.flush();
+ bos.close();
+ byte[] b = bos.toByteArray();
+ out.writeUInt(b.length);
+ out.write0(b, 0, b.length);
+ }
+ }
+ @Override
+ public Serializable parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_STREAM )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_STREAM, get " + b + ".");
+
+ UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(in.read0(in.readUInt()));
+ CompactedObjectInputStream ois = new CompactedObjectInputStream(bis);
+ try{ return (Serializable)ois.readObject(); }
+ catch(ClassNotFoundException e){ throw new IOException(StringUtils.toString(e)); }
+ }
+ };
+
+ static
+ {
+ addDesc(boolean[].class);
+ addDesc(byte[].class);
+ addDesc(char[].class);
+ addDesc(short[].class);
+ addDesc(int[].class);
+ addDesc(long[].class);
+ addDesc(float[].class);
+ addDesc(double[].class);
+
+ addDesc(Boolean.class);
+ addDesc(Byte.class);
+ addDesc(Character.class);
+ addDesc(Short.class);
+ addDesc(Integer.class);
+ addDesc(Long.class);
+ addDesc(Float.class);
+ addDesc(Double.class);
+
+ addDesc(String.class);
+ addDesc(String[].class);
+
+ addDesc(ArrayList.class);
+ addDesc(HashMap.class);
+ addDesc(HashSet.class);
+ addDesc(Date.class);
+ addDesc(java.sql.Date.class);
+ addDesc(java.sql.Time.class);
+ addDesc(java.sql.Timestamp.class);
+ addDesc(java.util.LinkedList.class);
+ addDesc(java.util.LinkedHashMap.class);
+ addDesc(java.util.LinkedHashSet.class);
+
+ register(byte[].class, new Builder<byte[]>(){
+ @Override
+ public Class<byte[]> getType(){ return byte[].class; }
+ @Override
+ public void writeTo(byte[] obj, GenericObjectOutput out) throws IOException{ out.writeBytes(obj); }
+ @Override
+ public byte[] parseFrom(GenericObjectInput in) throws IOException{ return in.readBytes(); }
+ });
+ register(Boolean.class, new Builder<Boolean>(){
+ @Override
+ public Class<Boolean> getType(){ return Boolean.class; }
+ @Override
+ public void writeTo(Boolean obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ out.write0(VARINT_N1);
+ else if( obj.booleanValue() )
+ out.write0(VARINT_1);
+ else
+ out.write0(VARINT_0);
+ }
+ @Override
+ public Boolean parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ switch( b )
+ {
+ case VARINT_N1: return null;
+ case VARINT_0: return Boolean.FALSE;
+ case VARINT_1: return Boolean.TRUE;
+ default: throw new IOException("Input format error, expect VARINT_N1|VARINT_0|VARINT_1, get " + b + ".");
+ }
+ }
+ });
+ register(Byte.class, new Builder<Byte>(){
+ @Override
+ public Class<Byte> getType(){ return Byte.class; }
+ @Override
+ public void writeTo(Byte obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeByte(obj.byteValue());
+ }
+ }
+ @Override
+ public Byte parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Byte.valueOf(in.readByte());
+ }
+ });
+ register(Character.class, new Builder<Character>(){
+ @Override
+ public Class<Character> getType(){ return Character.class; }
+ @Override
+ public void writeTo(Character obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeShort((short)obj.charValue());
+ }
+ }
+ @Override
+ public Character parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Character.valueOf((char)in.readShort());
+ }
+ });
+ register(Short.class, new Builder<Short>(){
+ @Override
+ public Class<Short> getType(){ return Short.class; }
+ @Override
+ public void writeTo(Short obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeShort(obj.shortValue());
+ }
+ }
+ @Override
+ public Short parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Short.valueOf(in.readShort());
+ }
+ });
+ register(Integer.class, new Builder<Integer>(){
+ @Override
+ public Class<Integer> getType(){ return Integer.class; }
+ @Override
+ public void writeTo(Integer obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeInt(obj.intValue());
+ }
+ }
+ @Override
+ public Integer parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Integer.valueOf(in.readInt());
+ }
+ });
+ register(Long.class, new Builder<Long>(){
+ @Override
+ public Class<Long> getType(){ return Long.class; }
+ @Override
+ public void writeTo(Long obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.longValue());
+ }
+ }
+ @Override
+ public Long parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Long.valueOf(in.readLong());
+ }
+ });
+ register(Float.class, new Builder<Float>(){
+ @Override
+ public Class<Float> getType(){ return Float.class; }
+ @Override
+ public void writeTo(Float obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeFloat(obj.floatValue());
+ }
+ }
+ @Override
+ public Float parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new Float(in.readFloat());
+ }
+ });
+ register(Double.class, new Builder<Double>(){
+ @Override
+ public Class<Double> getType(){ return Double.class; }
+ @Override
+ public void writeTo(Double obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeDouble(obj.doubleValue());
+ }
+ }
+ @Override
+ public Double parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new Double(in.readDouble());
+ }
+ });
+ register(String.class, new Builder<String>(){
+ @Override
+ public Class<String> getType(){ return String.class; }
+ @Override
+ public String parseFrom(GenericObjectInput in) throws IOException{ return in.readUTF(); }
+ @Override
+ public void writeTo(String obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj); }
+ });
+ register(StringBuilder.class, new Builder<StringBuilder>(){
+ @Override
+ public Class<StringBuilder> getType(){ return StringBuilder.class; }
+ @Override
+ public StringBuilder parseFrom(GenericObjectInput in) throws IOException{ return new StringBuilder(in.readUTF()); }
+ @Override
+ public void writeTo(StringBuilder obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); }
+ });
+ register(StringBuffer.class, new Builder<StringBuffer>(){
+ @Override
+ public Class<StringBuffer> getType(){ return StringBuffer.class; }
+ @Override
+ public StringBuffer parseFrom(GenericObjectInput in) throws IOException{ return new StringBuffer(in.readUTF()); }
+ @Override
+ public void writeTo(StringBuffer obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); }
+ });
+
+ // java.util
+ register(ArrayList.class, new Builder<ArrayList>(){
+ @Override
+ public Class<ArrayList> getType(){ return ArrayList.class; }
+ @Override
+ public void writeTo(ArrayList obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUES);
+ out.writeUInt(obj.size());
+ for( Object item : obj )
+ out.writeObject(item);
+ }
+ }
+ @Override
+ public ArrayList parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUES )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + ".");
+
+ int len = in.readUInt();
+ ArrayList ret = new ArrayList(len);
+ for(int i=0;i<len;i++)
+ ret.add(in.readObject());
+ return ret;
+ }
+ });
+ register(HashMap.class, new Builder<HashMap>(){
+ @Override
+ public Class<HashMap> getType(){ return HashMap.class; }
+ @Override
+ public void writeTo(HashMap obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_MAP);
+ out.writeUInt(obj.size());
+ for( Map.Entry entry : (Set<Map.Entry>)obj.entrySet() )
+ {
+ out.writeObject(entry.getKey());
+ out.writeObject(entry.getValue());
+ }
+ }
+ }
+ @Override
+ public HashMap parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_MAP )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_MAP, get " + b + ".");
+
+ int len = in.readUInt();
+ HashMap ret = new HashMap(len);
+ for(int i=0;i<len;i++)
+ ret.put(in.readObject(), in.readObject());
+ return ret;
+ }
+ });
+ register(HashSet.class, new Builder<HashSet>(){
+ @Override
+ public Class<HashSet> getType(){ return HashSet.class; }
+ @Override
+ public void writeTo(HashSet obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUES);
+ out.writeUInt(obj.size());
+ for( Object item : obj )
+ out.writeObject(item);
+ }
+ }
+ @Override
+ public HashSet parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUES )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + ".");
+
+ int len = in.readUInt();
+ HashSet ret = new HashSet(len);
+ for(int i=0;i<len;i++)
+ ret.add(in.readObject());
+ return ret;
+ }
+ });
+
+ register(Date.class, new Builder<Date>(){
+ @Override
+ public Class<Date> getType(){ return Date.class; }
+ @Override
+ public void writeTo(Date obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.getTime());
+ }
+ }
+ @Override
+ public Date parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new Date(in.readLong());
+ }
+ });
+
+ // java.sql
+ register(java.sql.Date.class, new Builder<java.sql.Date>(){
+ @Override
+ public Class<java.sql.Date> getType(){ return java.sql.Date.class; }
+ @Override
+ public void writeTo(java.sql.Date obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.getTime());
+ }
+ }
+ @Override
+ public java.sql.Date parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new java.sql.Date(in.readLong());
+ }
+ });
+ register(java.sql.Timestamp.class, new Builder<java.sql.Timestamp>(){
+ @Override
+ public Class<java.sql.Timestamp> getType(){ return java.sql.Timestamp.class; }
+ @Override
+ public void writeTo(java.sql.Timestamp obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.getTime());
+ }
+ }
+ @Override
+ public java.sql.Timestamp parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new java.sql.Timestamp(in.readLong());
+ }
+ });
+ register(java.sql.Time.class, new Builder<java.sql.Time>(){
+ @Override
+ public Class<java.sql.Time> getType(){ return java.sql.Time.class; }
+ @Override
+ public void writeTo(java.sql.Time obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.getTime());
+ }
+ }
+ @Override
+ public java.sql.Time parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new java.sql.Time(in.readLong());
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.java
new file mode 100644
index 0000000..3b8a240
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+public class DubboSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 1;
+ }
+
+ public String getContentType() {
+ return "x-application/dubbo";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
+ return new GenericObjectOutput(out);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream is) throws IOException {
+ return new GenericObjectInput(is);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataFlags.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataFlags.java
new file mode 100644
index 0000000..efab960
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataFlags.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+/**
+ * Constants.
+ *
+ * @author qian.lei
+ */
+
+public interface GenericDataFlags
+{
+ // prefix three bits
+ byte VARINT = 0, OBJECT = (byte)0x80;
+
+ // varint tag
+ byte VARINT8 = VARINT, VARINT16 = VARINT | 1, VARINT24 = VARINT | 2, VARINT32 = VARINT | 3;
+
+ byte VARINT40 = VARINT | 4, VARINT48 = VARINT | 5, VARINT56 = VARINT | 6, VARINT64 = VARINT | 7;
+
+ // varint contants
+ byte VARINT_NF = VARINT | 10, VARINT_NE = VARINT | 11, VARINT_ND = VARINT | 12;
+
+ byte VARINT_NC = VARINT | 13, VARINT_NB = VARINT | 14, VARINT_NA = VARINT | 15, VARINT_N9 = VARINT | 16;
+
+ byte VARINT_N8 = VARINT | 17, VARINT_N7 = VARINT | 18, VARINT_N6 = VARINT | 19, VARINT_N5 = VARINT | 20;
+
+ byte VARINT_N4 = VARINT | 21, VARINT_N3 = VARINT | 22, VARINT_N2 = VARINT | 23, VARINT_N1 = VARINT | 24;
+
+ byte VARINT_0 = VARINT | 25, VARINT_1 = VARINT | 26, VARINT_2 = VARINT | 27, VARINT_3 = VARINT | 28;
+
+ byte VARINT_4 = VARINT | 29, VARINT_5 = VARINT | 30, VARINT_6 = VARINT | 31, VARINT_7 = VARINT | 32;
+
+ byte VARINT_8 = VARINT | 33, VARINT_9 = VARINT | 34, VARINT_A = VARINT | 35, VARINT_B = VARINT | 36;
+
+ byte VARINT_C = VARINT | 37, VARINT_D = VARINT | 38, VARINT_E = VARINT | 39, VARINT_F = VARINT | 40;
+
+ byte VARINT_10 = VARINT | 41, VARINT_11 = VARINT | 42, VARINT_12 = VARINT | 43, VARINT_13 = VARINT | 44;
+
+ byte VARINT_14 = VARINT | 45, VARINT_15 = VARINT | 46, VARINT_16 = VARINT | 47, VARINT_17 = VARINT | 48;
+
+ byte VARINT_18 = VARINT | 49, VARINT_19 = VARINT | 50, VARINT_1A = VARINT | 51, VARINT_1B = VARINT | 52;
+
+ byte VARINT_1C = VARINT | 53, VARINT_1D = VARINT | 54, VARINT_1E = VARINT | 55, VARINT_1F = VARINT | 56;
+
+ // object tag
+ byte OBJECT_REF = OBJECT | 1, OBJECT_STREAM = OBJECT | 2, OBJECT_BYTES = OBJECT | 3;
+
+ byte OBJECT_VALUE = OBJECT | 4, OBJECT_VALUES = OBJECT | 5, OBJECT_MAP = OBJECT | 6;
+
+ byte OBJECT_DESC = OBJECT | 10, OBJECT_DESC_ID = OBJECT | 11;
+
+ // object constants
+ byte OBJECT_NULL = OBJECT | 20, OBJECT_DUMMY = OBJECT | 21;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataInput.java
new file mode 100644
index 0000000..a9dc954
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataInput.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UTFDataFormatException;
+
+import com.alibaba.dubbo.common.serialize.DataInput;
+
+/**
+ * Default DataInput impl.
+ * Not thread-safe.
+ *
+ * @author qian.lei
+ */
+
+public class GenericDataInput implements DataInput, GenericDataFlags
+{
+ private static final String EMPTY_STRING = "";
+
+ private static final byte[] EMPTY_BYTES = {};
+
+ private final InputStream mInput;
+
+ private final byte[] mBuffer;
+
+ private int mRead = 0;
+
+ private int mPosition = 0;
+
+ public GenericDataInput(InputStream is)
+ {
+ this(is, 1024);
+ }
+
+ public GenericDataInput(InputStream is, int buffSize)
+ {
+ mInput = is;
+ mBuffer = new byte[buffSize];
+ }
+
+ public boolean readBool() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case VARINT_0: return false;
+ case VARINT_1: return true;
+ default:
+ throw new IOException("Tag error, expect BYTE_TRUE|BYTE_FALSE, but get " + b);
+ }
+ }
+
+ public byte readByte() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case VARINT8:
+ return read0();
+ case VARINT_0: return 0; case VARINT_1: return 1; case VARINT_2: return 2; case VARINT_3: return 3;
+ case VARINT_4: return 4; case VARINT_5: return 5; case VARINT_6: return 6; case VARINT_7: return 7;
+ case VARINT_8: return 8; case VARINT_9: return 9; case VARINT_A: return 10; case VARINT_B: return 11;
+ case VARINT_C: return 12; case VARINT_D: return 13; case VARINT_E: return 14; case VARINT_F: return 15;
+ case VARINT_10: return 16; case VARINT_11: return 17; case VARINT_12: return 18; case VARINT_13: return 19;
+ case VARINT_14: return 20; case VARINT_15: return 21; case VARINT_16: return 22; case VARINT_17: return 23;
+ case VARINT_18: return 24; case VARINT_19: return 25; case VARINT_1A: return 26; case VARINT_1B: return 27;
+ case VARINT_1C: return 28; case VARINT_1D: return 29; case VARINT_1E: return 30; case VARINT_1F: return 31;
+ default:
+ throw new IOException("Tag error, expect VARINT, but get " + b);
+ }
+ }
+
+ public short readShort() throws IOException
+ {
+ return (short)readVarint32();
+ }
+
+ public int readInt() throws IOException
+ {
+ return readVarint32();
+ }
+
+ public long readLong() throws IOException
+ {
+ return readVarint64();
+ }
+
+ public float readFloat() throws IOException
+ {
+ return Float.intBitsToFloat(readVarint32());
+ }
+
+ public double readDouble() throws IOException
+ {
+ return Double.longBitsToDouble(readVarint64());
+ }
+
+ public String readUTF() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case OBJECT_BYTES:
+ int len = readUInt();
+ StringBuilder sb = new StringBuilder();
+
+ for(int i=0;i<len;i++)
+ {
+ byte b1 = read0();
+ if( (b1 & 0x80) == 0 )
+ {
+ sb.append((char)b1);
+ }
+ else if( (b1 & 0xE0) == 0xC0 )
+ {
+ byte b2 = read0();
+ sb.append((char)(((b1 & 0x1F) << 6) | (b2 & 0x3F)));
+ }
+ else if( (b1 & 0xF0) == 0xE0 )
+ {
+ byte b2 = read0(), b3 = read0();
+ sb.append((char)(((b1 & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F)));
+ }
+ else
+ throw new UTFDataFormatException("Bad utf-8 encoding at " + b1);
+ }
+ return sb.toString();
+ case OBJECT_NULL: return null;
+ case OBJECT_DUMMY: return EMPTY_STRING;
+ default:
+ throw new IOException("Tag error, expect BYTES|BYTES_NULL|BYTES_EMPTY, but get " + b);
+ }
+ }
+
+ public byte[] readBytes() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case OBJECT_BYTES: return read0(readUInt());
+ case OBJECT_NULL: return null;
+ case OBJECT_DUMMY: return EMPTY_BYTES;
+ default:
+ throw new IOException("Tag error, expect BYTES|BYTES_NULL|BYTES_EMPTY, but get " + b);
+ }
+ }
+
+ public int readUInt() throws IOException
+ {
+ byte tmp = read0();
+ if( tmp < 0 )
+ return tmp & 0x7f;
+
+ int ret = tmp & 0x7f;
+ if( ( tmp = read0() ) < 0 )
+ {
+ ret |= ( tmp & 0x7f ) << 7;
+ }
+ else
+ {
+ ret |= tmp << 7;
+ if( ( tmp = read0() ) < 0 )
+ {
+ ret |= ( tmp & 0x7f ) << 14;
+ }
+ else
+ {
+ ret |= tmp << 14;
+ if( ( tmp = read0() ) < 0 )
+ {
+ ret |= ( tmp & 0x7f ) << 21;
+ }
+ else
+ {
+ ret |= tmp << 21;
+ ret |= ( read0() & 0x7f ) << 28;
+ }
+ }
+ }
+ return ret;
+ }
+
+ protected byte read0() throws IOException
+ {
+ if( mPosition == mRead )
+ fillBuffer();
+
+ return mBuffer[mPosition++];
+ }
+
+ protected byte[] read0(int len) throws IOException
+ {
+ int rem = mRead - mPosition;
+ byte[] ret = new byte[len];
+ if( len <= rem )
+ {
+ System.arraycopy(mBuffer, mPosition, ret, 0, len);
+ mPosition += len;
+ }
+ else
+ {
+ System.arraycopy(mBuffer, mPosition, ret, 0, rem);
+ mPosition = mRead;
+
+ len -= rem;
+ int read, pos = rem;
+
+ while( len > 0 )
+ {
+ read = mInput.read(ret, pos, len);
+ if( read == -1 )
+ throw new EOFException();
+ pos += read;
+ len -= read;
+ }
+ }
+ return ret;
+ }
+
+ private int readVarint32() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case VARINT8:
+ return read0();
+ case VARINT16:
+ {
+ byte b1 = read0(), b2 = read0();
+ return (short)( ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) );
+ }
+ case VARINT24:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0();
+ int ret = ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) | ( ( b3 & 0xff ) << 16 );
+ if( b3 < 0 )
+ return ret | 0xff000000;
+ return ret;
+ }
+ case VARINT32:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0();
+ return ( ( b1 & 0xff ) |
+ ( ( b2 & 0xff ) << 8 ) |
+ ( ( b3 & 0xff ) << 16 ) |
+ ( ( b4 & 0xff ) << 24 ) );
+ }
+ case VARINT_NF: return -15; case VARINT_NE: return -14; case VARINT_ND: return -13;
+ case VARINT_NC: return -12; case VARINT_NB: return -11; case VARINT_NA: return -10; case VARINT_N9: return -9;
+ case VARINT_N8: return -8; case VARINT_N7: return -7; case VARINT_N6: return -6; case VARINT_N5: return -5;
+ case VARINT_N4: return -4; case VARINT_N3: return -3; case VARINT_N2: return -2; case VARINT_N1: return -1;
+ case VARINT_0: return 0; case VARINT_1: return 1; case VARINT_2: return 2; case VARINT_3: return 3;
+ case VARINT_4: return 4; case VARINT_5: return 5; case VARINT_6: return 6; case VARINT_7: return 7;
+ case VARINT_8: return 8; case VARINT_9: return 9; case VARINT_A: return 10; case VARINT_B: return 11;
+ case VARINT_C: return 12; case VARINT_D: return 13; case VARINT_E: return 14; case VARINT_F: return 15;
+ case VARINT_10: return 16; case VARINT_11: return 17; case VARINT_12: return 18; case VARINT_13: return 19;
+ case VARINT_14: return 20; case VARINT_15: return 21; case VARINT_16: return 22; case VARINT_17: return 23;
+ case VARINT_18: return 24; case VARINT_19: return 25; case VARINT_1A: return 26; case VARINT_1B: return 27;
+ case VARINT_1C: return 28; case VARINT_1D: return 29; case VARINT_1E: return 30; case VARINT_1F: return 31;
+ default:
+ throw new IOException("Tag error, expect VARINT, but get " + b);
+ }
+ }
+
+ private long readVarint64() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case VARINT8:
+ return read0();
+ case VARINT16:
+ {
+ byte b1 = read0(), b2 = read0();
+ return (short)( ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) );
+ }
+ case VARINT24:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0();
+ int ret = ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) | ( ( b3 & 0xff ) << 16 );
+ if( b3 < 0 )
+ return ret | 0xff000000;
+ return ret;
+ }
+ case VARINT32:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0();
+ return ( ( b1 & 0xff ) |
+ ( ( b2 & 0xff ) << 8 ) |
+ ( ( b3 & 0xff ) << 16 ) |
+ ( ( b4 & 0xff ) << 24 ) );
+ }
+ case VARINT40:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0(), b5 = read0();
+ long ret = ( (long)b1 & 0xff ) |
+ ( ( (long)b2 & 0xff ) << 8 ) |
+ ( ( (long)b3 & 0xff ) << 16 ) |
+ ( ( (long)b4 & 0xff ) << 24 ) |
+ ( ( (long)b5 & 0xff ) << 32 );
+ if( b5 < 0 )
+ return ret | 0xffffff0000000000l;
+ return ret;
+ }
+ case VARINT48:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0(), b5 = read0(), b6 = read0();
+ long ret = ( (long)b1 & 0xff ) |
+ ( ( (long)b2 & 0xff ) << 8 ) |
+ ( ( (long)b3 & 0xff ) << 16 ) |
+ ( ( (long)b4 & 0xff ) << 24 ) |
+ ( ( (long)b5 & 0xff ) << 32 ) |
+ ( ( (long)b6 & 0xff ) << 40 );
+ if( b6 < 0 )
+ return ret | 0xffff000000000000l;
+ return ret;
+ }
+ case VARINT56:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0(), b5 = read0(), b6 = read0(), b7 = read0();
+ long ret = ( (long)b1 & 0xff ) |
+ ( ( (long)b2 & 0xff ) << 8 ) |
+ ( ( (long)b3 & 0xff ) << 16 ) |
+ ( ( (long)b4 & 0xff ) << 24 ) |
+ ( ( (long)b5 & 0xff ) << 32 ) |
+ ( ( (long)b6 & 0xff ) << 40 ) |
+ ( ( (long)b7 & 0xff ) << 48 );
+ if( b7 < 0 )
+ return ret | 0xff00000000000000l;
+ return ret;
+ }
+ case VARINT64:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0();
+ byte b5 = read0(), b6 = read0(), b7 = read0(), b8 = read0();
+ return ( ( (long)b1 & 0xff ) |
+ ( ( (long)b2 & 0xff ) << 8 ) |
+ ( ( (long)b3 & 0xff ) << 16 ) |
+ ( ( (long)b4 & 0xff ) << 24 ) |
+ ( ( (long)b5 & 0xff ) << 32 ) |
+ ( ( (long)b6 & 0xff ) << 40 ) |
+ ( ( (long)b7 & 0xff ) << 48 ) |
+ ( ( (long)b8 & 0xff ) << 56 ) );
+ }
+ case VARINT_NF: return -15; case VARINT_NE: return -14; case VARINT_ND: return -13;
+ case VARINT_NC: return -12; case VARINT_NB: return -11; case VARINT_NA: return -10; case VARINT_N9: return -9;
+ case VARINT_N8: return -8; case VARINT_N7: return -7; case VARINT_N6: return -6; case VARINT_N5: return -5;
+ case VARINT_N4: return -4; case VARINT_N3: return -3; case VARINT_N2: return -2; case VARINT_N1: return -1;
+ case VARINT_0: return 0; case VARINT_1: return 1; case VARINT_2: return 2; case VARINT_3: return 3;
+ case VARINT_4: return 4; case VARINT_5: return 5; case VARINT_6: return 6; case VARINT_7: return 7;
+ case VARINT_8: return 8; case VARINT_9: return 9; case VARINT_A: return 10; case VARINT_B: return 11;
+ case VARINT_C: return 12; case VARINT_D: return 13; case VARINT_E: return 14; case VARINT_F: return 15;
+ case VARINT_10: return 16; case VARINT_11: return 17; case VARINT_12: return 18; case VARINT_13: return 19;
+ case VARINT_14: return 20; case VARINT_15: return 21; case VARINT_16: return 22; case VARINT_17: return 23;
+ case VARINT_18: return 24; case VARINT_19: return 25; case VARINT_1A: return 26; case VARINT_1B: return 27;
+ case VARINT_1C: return 28; case VARINT_1D: return 29; case VARINT_1E: return 30; case VARINT_1F: return 31;
+ default:
+ throw new IOException("Tag error, expect VARINT, but get " + b);
+ }
+ }
+
+ private void fillBuffer() throws IOException
+ {
+ mPosition = 0;
+ mRead = mInput.read(mBuffer);
+
+ if( mRead == -1 )
+ {
+ mRead = 0;
+ throw new EOFException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataOutput.java
new file mode 100644
index 0000000..b50ca1f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataOutput.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.serialize.DataOutput;
+
+/**
+ * Default data output impl.
+ * Not thread-safe.
+ *
+ * @author qian.lei
+ */
+
+public class GenericDataOutput implements DataOutput, GenericDataFlags
+{
+ private static final int CHAR_BUF_SIZE = 256;
+
+ private final byte[] mBuffer, mTemp = new byte[9];
+
+ private final char[] mCharBuf = new char[CHAR_BUF_SIZE];
+
+ private final OutputStream mOutput;
+
+ private final int mLimit;
+
+ private int mPosition = 0;
+
+ public GenericDataOutput(OutputStream out)
+ {
+ this(out, 1024);
+ }
+
+ public GenericDataOutput(OutputStream out, int buffSize)
+ {
+ mOutput = out;
+ mLimit = buffSize;
+ mBuffer = new byte[buffSize];
+ }
+
+ public void writeBool(boolean v) throws IOException
+ {
+ write0( v ? VARINT_1 : VARINT_0 );
+ }
+
+ public void writeByte(byte v) throws IOException
+ {
+ switch( v )
+ {
+ case 0: write0(VARINT_0); break; case 1: write0(VARINT_1); break; case 2: write0(VARINT_2); break; case 3: write0(VARINT_3); break;
+ case 4: write0(VARINT_4); break; case 5: write0(VARINT_5); break; case 6: write0(VARINT_6); break; case 7: write0(VARINT_7); break;
+ case 8: write0(VARINT_8); break; case 9: write0(VARINT_9); break; case 10: write0(VARINT_A); break; case 11: write0(VARINT_B); break;
+ case 12: write0(VARINT_C); break; case 13: write0(VARINT_D); break; case 14: write0(VARINT_E); break; case 15: write0(VARINT_F); break;
+ case 16: write0(VARINT_10); break; case 17: write0(VARINT_11); break; case 18: write0(VARINT_12); break; case 19: write0(VARINT_13); break;
+ case 20: write0(VARINT_14); break; case 21: write0(VARINT_15); break; case 22: write0(VARINT_16); break; case 23: write0(VARINT_17); break;
+ case 24: write0(VARINT_18); break; case 25: write0(VARINT_19); break; case 26: write0(VARINT_1A); break; case 27: write0(VARINT_1B); break;
+ case 28: write0(VARINT_1C); break; case 29: write0(VARINT_1D); break; case 30: write0(VARINT_1E); break; case 31: write0(VARINT_1F); break;
+ default:
+ write0(VARINT8);
+ write0(v);
+ }
+ }
+
+ public void writeShort(short v) throws IOException
+ {
+ writeVarint32(v);
+ }
+
+ public void writeInt(int v) throws IOException
+ {
+ writeVarint32(v);
+ }
+
+ public void writeLong(long v) throws IOException
+ {
+ writeVarint64(v);
+ }
+
+ public void writeFloat(float v) throws IOException
+ {
+ writeVarint32(Float.floatToRawIntBits(v));
+ }
+
+ public void writeDouble(double v) throws IOException
+ {
+ writeVarint64(Double.doubleToRawLongBits(v));
+ }
+
+ public void writeUTF(String v) throws IOException
+ {
+ if( v == null )
+ {
+ write0(OBJECT_NULL);
+ }
+ else
+ {
+ int len = v.length();
+ if( len == 0 )
+ {
+ write0(OBJECT_DUMMY);
+ }
+ else
+ {
+ write0(OBJECT_BYTES);
+ writeUInt(len);
+
+ int off = 0, limit = mLimit - 3, size;
+ char[] buf = mCharBuf;
+ do
+ {
+ size = Math.min(len-off, CHAR_BUF_SIZE);
+ v.getChars(off, off+size, buf, 0);
+
+ for(int i=0;i<size;i++)
+ {
+ char c = buf[i];
+ if( mPosition > limit )
+ {
+ if( c < 0x80 )
+ {
+ write0((byte)c);
+ }
+ else if( c < 0x800 )
+ {
+ write0((byte)(0xC0 | ((c >> 6) & 0x1F)));
+ write0((byte)(0x80 | (c & 0x3F)));
+ }
+ else
+ {
+ write0((byte)(0xE0 | ((c >> 12) & 0x0F)));
+ write0((byte)(0x80 | ((c >> 6) & 0x3F)));
+ write0((byte)(0x80 | (c & 0x3F)));
+ }
+ }
+ else
+ {
+ if( c < 0x80 )
+ {
+ mBuffer[mPosition++] = (byte)c;
+ }
+ else if( c < 0x800 )
+ {
+ mBuffer[mPosition++] = (byte)(0xC0 | ((c >> 6) & 0x1F));
+ mBuffer[mPosition++] = (byte)(0x80 | (c & 0x3F));
+ }
+ else
+ {
+ mBuffer[mPosition++] = (byte)(0xE0 | ((c >> 12) & 0x0F));
+ mBuffer[mPosition++] = (byte)(0x80 | ((c >> 6) & 0x3F));
+ mBuffer[mPosition++] = (byte)(0x80 | (c & 0x3F));
+ }
+ }
+ }
+ off += size;
+ }
+ while( off < len );
+ }
+ }
+ }
+
+ public void writeBytes(byte[] b) throws IOException
+ {
+ if( b == null )
+ write0(OBJECT_NULL);
+ else
+ writeBytes(b, 0, b.length);
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException
+ {
+ if( len == 0 )
+ {
+ write0(OBJECT_DUMMY);
+ }
+ else
+ {
+ write0(OBJECT_BYTES);
+ writeUInt(len);
+ write0(b, off, len);
+ }
+ }
+
+ public void flushBuffer() throws IOException
+ {
+ if( mPosition > 0 )
+ {
+ mOutput.write(mBuffer, 0, mPosition);
+ mPosition = 0;
+ }
+ }
+
+ public void writeUInt(int v) throws IOException
+ {
+ byte tmp;
+ while( true )
+ {
+ tmp = (byte)( v & 0x7f );
+ if( ( v >>>= 7 ) == 0 )
+ {
+ write0( (byte)( tmp | 0x80 ) );
+ return;
+ }
+ else
+ {
+ write0(tmp);
+ }
+ }
+ }
+
+ protected void write0(byte b) throws IOException
+ {
+ if( mPosition == mLimit )
+ flushBuffer();
+
+ mBuffer[mPosition++] = b;
+ }
+
+ protected void write0(byte[] b, int off, int len) throws IOException
+ {
+ int rem = mLimit - mPosition;
+ if( rem > len )
+ {
+ System.arraycopy(b, off, mBuffer, mPosition, len);
+ mPosition += len;
+ }
+ else
+ {
+ System.arraycopy(b, off, mBuffer, mPosition, rem);
+ mPosition = mLimit;
+ flushBuffer();
+
+ off += rem;
+ len -= rem;
+
+ if( mLimit > len )
+ {
+ System.arraycopy(b, off, mBuffer, 0, len);
+ mPosition = len;
+ }
+ else
+ {
+ mOutput.write(b, off, len);
+ }
+ }
+ }
+
+ private void writeVarint32(int v) throws IOException
+ {
+ switch( v )
+ {
+ case -15: write0(VARINT_NF); break; case -14: write0(VARINT_NE); break; case -13: write0(VARINT_ND); break;
+ case -12: write0(VARINT_NC); break; case -11: write0(VARINT_NB); break; case -10: write0(VARINT_NA); break; case -9: write0(VARINT_N9); break;
+ case -8: write0(VARINT_N8); break; case -7: write0(VARINT_N7); break; case -6: write0(VARINT_N6); break; case -5: write0(VARINT_N5); break;
+ case -4: write0(VARINT_N4); break; case -3: write0(VARINT_N3); break; case -2: write0(VARINT_N2); break; case -1: write0(VARINT_N1); break;
+ case 0: write0(VARINT_0); break; case 1: write0(VARINT_1); break; case 2: write0(VARINT_2); break; case 3: write0(VARINT_3); break;
+ case 4: write0(VARINT_4); break; case 5: write0(VARINT_5); break; case 6: write0(VARINT_6); break; case 7: write0(VARINT_7); break;
+ case 8: write0(VARINT_8); break; case 9: write0(VARINT_9); break; case 10: write0(VARINT_A); break; case 11: write0(VARINT_B); break;
+ case 12: write0(VARINT_C); break; case 13: write0(VARINT_D); break; case 14: write0(VARINT_E); break; case 15: write0(VARINT_F); break;
+ case 16: write0(VARINT_10); break; case 17: write0(VARINT_11); break; case 18: write0(VARINT_12); break; case 19: write0(VARINT_13); break;
+ case 20: write0(VARINT_14); break; case 21: write0(VARINT_15); break; case 22: write0(VARINT_16); break; case 23: write0(VARINT_17); break;
+ case 24: write0(VARINT_18); break; case 25: write0(VARINT_19); break; case 26: write0(VARINT_1A); break; case 27: write0(VARINT_1B); break;
+ case 28: write0(VARINT_1C); break; case 29: write0(VARINT_1D); break; case 30: write0(VARINT_1E); break; case 31: write0(VARINT_1F); break;
+ default:
+ int t = v, ix = 0;
+ byte[] b = mTemp;
+
+ while( true )
+ {
+ b[++ix] = (byte)( v & 0xff );
+ if( ( v >>>= 8 ) == 0 )
+ break;
+ }
+
+ if( t > 0 )
+ {
+ // [ 0a e2 => 0a e2 00 ] [ 92 => 92 00 ]
+ if( b[ix] < 0 )
+ b[++ix] = 0;
+ }
+ else
+ {
+ // [ 01 ff ff ff => 01 ff ] [ e0 ff ff ff => e0 ]
+ while( b[ix] == (byte)0xff && b[ix-1] < 0 )
+ ix--;
+ }
+
+ b[0] = (byte)( VARINT + ix - 1 );
+ write0(b, 0, ix+1);
+ }
+ }
+
+ private void writeVarint64(long v) throws IOException
+ {
+ int i = (int)v;
+ if( v == i )
+ {
+ writeVarint32(i);
+ }
+ else
+ {
+ long t = v;
+ int ix = 0;
+ byte[] b = mTemp;
+
+ while( true )
+ {
+ b[++ix] = (byte)( v & 0xff );
+ if( ( v >>>= 8 ) == 0 )
+ break;
+ }
+
+ if( t > 0 )
+ {
+ // [ 0a e2 => 0a e2 00 ] [ 92 => 92 00 ]
+ if( b[ix] < 0 )
+ b[++ix] = 0;
+ }
+ else
+ {
+ // [ 01 ff ff ff => 01 ff ] [ e0 ff ff ff => e0 ]
+ while( b[ix] == (byte)0xff && b[ix-1] < 0 )
+ ix--;
+ }
+
+ b[0] = (byte)( VARINT + ix - 1 );
+ write0(b, 0, ix+1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectInput.java
new file mode 100644
index 0000000..b8b220a
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectInput.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.io.ClassDescriptorMapper;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+/**
+ * Generic Object Input.
+ *
+ * @author qian.lei
+ */
+public class GenericObjectInput extends GenericDataInput implements ObjectInput
+{
+ private static Object SKIPPED_OBJECT = new Object();
+
+ private ClassDescriptorMapper mMapper;
+
+ private List<Object> mRefs = new ArrayList<Object>();
+
+ public GenericObjectInput(InputStream is)
+ {
+ this(is, Builder.DEFAULT_CLASS_DESCRIPTOR_MAPPER);
+ }
+
+ public GenericObjectInput(InputStream is, ClassDescriptorMapper mapper)
+ {
+ super(is);
+ mMapper = mapper;
+ }
+
+ public GenericObjectInput(InputStream is, int buffSize)
+ {
+ this(is, buffSize, Builder.DEFAULT_CLASS_DESCRIPTOR_MAPPER);
+ }
+
+ public GenericObjectInput(InputStream is, int buffSize, ClassDescriptorMapper mapper)
+ {
+ super(is, buffSize);
+ mMapper = mapper;
+ }
+
+ public Object readObject() throws IOException
+ {
+ String desc;
+ byte b = read0();
+
+ switch( b )
+ {
+ case OBJECT_NULL:
+ return null;
+ case OBJECT_DUMMY:
+ return new Object();
+ case OBJECT_DESC:
+ {
+ desc = readUTF();
+ break;
+ }
+ case OBJECT_DESC_ID:
+ {
+ int index = readUInt();
+ desc = mMapper.getDescriptor(index);
+ if( desc == null )
+ throw new IOException("Can not find desc id: " + index );
+ break;
+ }
+ default:
+ throw new IOException("Flag error, expect OBJECT_NULL|OBJECT_DUMMY|OBJECT_DESC|OBJECT_DESC_ID, get " + b);
+ }
+ try
+ {
+ Class<?> c = ReflectUtils.desc2class(desc);
+ return Builder.register(c).parseFrom(this);
+ }
+ catch(ClassNotFoundException e)
+ {
+ throw new IOException("Read object failed, class not found. " + StringUtils.toString(e));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(Class<T> cls) throws IOException,ClassNotFoundException
+ {
+ return (T)readObject();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException
+ {
+ return (T)readObject();
+ }
+
+ public void addRef(Object obj)
+ {
+ mRefs.add(obj);
+ }
+
+ public Object getRef(int index) throws IOException
+ {
+ if( index < 0 || index >= mRefs.size() )
+ return null;
+
+ Object ret = mRefs.get(index);
+ if( ret == SKIPPED_OBJECT )
+ throw new IOException("Ref skipped-object.");
+ return ret;
+ }
+
+ public void skipAny() throws IOException
+ {
+ byte b = read0();
+ switch( b )
+ {
+ case VARINT_NF: case VARINT_NE: case VARINT_ND: case VARINT_NC: case VARINT_NB: case VARINT_NA: case VARINT_N9:
+ case VARINT_N8: case VARINT_N7: case VARINT_N6: case VARINT_N5: case VARINT_N4: case VARINT_N3: case VARINT_N2: case VARINT_N1:
+ case VARINT_0: case VARINT_1: case VARINT_2: case VARINT_3: case VARINT_4: case VARINT_5: case VARINT_6: case VARINT_7:
+ case VARINT_8: case VARINT_9: case VARINT_A: case VARINT_B: case VARINT_C: case VARINT_D: case VARINT_E: case VARINT_F:
+ case VARINT_10: case VARINT_11: case VARINT_12: case VARINT_13: case VARINT_14: case VARINT_15: case VARINT_16: case VARINT_17:
+ case VARINT_18: case VARINT_19: case VARINT_1A: case VARINT_1B: case VARINT_1C: case VARINT_1D: case VARINT_1E: case VARINT_1F:
+ case OBJECT_NULL: case OBJECT_DUMMY:
+ break;
+ case VARINT8:
+ {
+ read0();
+ break;
+ }
+ case VARINT16:
+ {
+ read0(); read0();
+ break;
+ }
+ case VARINT24:
+ {
+ read0(); read0(); read0();
+ break;
+ }
+ case VARINT32:
+ {
+ read0(); read0(); read0(); read0();
+ break;
+ }
+ case VARINT40:
+ {
+ read0(); read0(); read0(); read0(); read0();
+ break;
+ }
+ case VARINT48:
+ {
+ read0(); read0(); read0(); read0(); read0(); read0();
+ break;
+ }
+ case VARINT56:
+ {
+ read0(); read0(); read0(); read0(); read0(); read0(); read0();
+ break;
+ }
+ case VARINT64:
+ {
+ read0(); read0(); read0(); read0(); read0(); read0(); read0(); read0();
+ break;
+ }
+ case OBJECT:
+ {
+ addRef(SKIPPED_OBJECT);
+ int len = readUInt();
+ for(int i=0;i<len;i++)
+ skipAny();
+ break;
+ }
+ case OBJECT_REF:
+ {
+ readUInt();
+ break;
+ }
+ case OBJECT_STREAM: case OBJECT_BYTES:
+ {
+ read0(readUInt());
+ break;
+ }
+ case OBJECT_VALUE:
+ {
+ skipAny();
+ break;
+ }
+ case OBJECT_VALUES:
+ {
+ int len = readUInt();
+ for(int i=0;i<len;i++)
+ skipAny();
+ break;
+ }
+ case OBJECT_MAP:
+ {
+ int len = readUInt();
+ for(int i=0;i<len;i++)
+ {
+ skipAny(); // skip key
+ skipAny(); // skip value
+ }
+ break;
+ }
+ case OBJECT_DESC:
+ {
+ readUTF();
+ int len = readUInt();
+ for(int i=0;i<len;i++)
+ skipAny();
+ break;
+ }
+ case OBJECT_DESC_ID:
+ {
+ readUInt();
+ int len = readUInt();
+ for(int i=0;i<len;i++)
+ skipAny();
+ break;
+ }
+ default:
+ throw new IOException("Flag error, get " + b);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectOutput.java
new file mode 100644
index 0000000..e2f8752
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectOutput.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.io.ClassDescriptorMapper;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+
+/**
+ * Generic Object Output.
+ *
+ * @author qian.lei
+ */
+
+public class GenericObjectOutput extends GenericDataOutput implements ObjectOutput
+{
+ private ClassDescriptorMapper mMapper;
+
+ private Map<Object, Integer> mRefs = new ConcurrentHashMap<Object, Integer>();
+
+ private final boolean isAllowNonSerializable;
+
+ public GenericObjectOutput(OutputStream out)
+ {
+ this(out, Builder.DEFAULT_CLASS_DESCRIPTOR_MAPPER);
+ }
+
+ public GenericObjectOutput(OutputStream out, ClassDescriptorMapper mapper)
+ {
+ super(out);
+ mMapper = mapper;
+ isAllowNonSerializable = false;
+ }
+
+ public GenericObjectOutput(OutputStream out, int buffSize)
+ {
+ this(out, buffSize, Builder.DEFAULT_CLASS_DESCRIPTOR_MAPPER, false);
+ }
+
+ public GenericObjectOutput(OutputStream out, int buffSize, ClassDescriptorMapper mapper)
+ {
+ this(out, buffSize, mapper, false);
+ }
+
+ public GenericObjectOutput(OutputStream out, int buffSize, ClassDescriptorMapper mapper, boolean isAllowNonSerializable)
+ {
+ super(out, buffSize);
+ mMapper = mapper;
+ this.isAllowNonSerializable = isAllowNonSerializable;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void writeObject(Object obj) throws IOException
+ {
+ if( obj == null )
+ {
+ write0(OBJECT_NULL);
+ return;
+ }
+
+ Class<?> c = obj.getClass();
+ if( c == Object.class )
+ {
+ write0(OBJECT_DUMMY);
+ }
+ else
+ {
+ String desc = ReflectUtils.getDesc(c);
+ int index = mMapper.getDescriptorIndex(desc);
+ if( index < 0 )
+ {
+ write0(OBJECT_DESC);
+ writeUTF(desc);
+ }
+ else
+ {
+ write0(OBJECT_DESC_ID);
+ writeUInt(index);
+ }
+ Builder b = Builder.register(c, isAllowNonSerializable);
+ b.writeTo(obj, this);
+ }
+ }
+
+ public void addRef(Object obj)
+ {
+ mRefs.put(obj, mRefs.size());
+ }
+
+ public int getRef(Object obj)
+ {
+ Integer ref = mRefs.get(obj);
+ if( ref == null )
+ return -1;
+ return ref.intValue();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectInput.java
new file mode 100644
index 0000000..c3db300
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectInput.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.hessian;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Type;
+
+import com.alibaba.com.caucho.hessian.io.Hessian2Input;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+
+/**
+ * Hessian2 Object input.
+ *
+ * @author qian.lei
+ */
+
+public class Hessian2ObjectInput implements ObjectInput
+{
+ private final Hessian2Input mH2i;
+
+ public Hessian2ObjectInput(InputStream is)
+ {
+ mH2i = new Hessian2Input(is);
+ mH2i.setSerializerFactory(Hessian2SerializerFactory.SERIALIZER_FACTORY);
+ }
+
+ public boolean readBool() throws IOException
+ {
+ return mH2i.readBoolean();
+ }
+
+ public byte readByte() throws IOException
+ {
+ return (byte)mH2i.readInt();
+ }
+
+ public short readShort() throws IOException
+ {
+ return (short)mH2i.readInt();
+ }
+
+ public int readInt() throws IOException
+ {
+ return mH2i.readInt();
+ }
+
+ public long readLong() throws IOException
+ {
+ return mH2i.readLong();
+ }
+
+ public float readFloat() throws IOException
+ {
+ return (float)mH2i.readDouble();
+ }
+
+ public double readDouble() throws IOException
+ {
+ return mH2i.readDouble();
+ }
+
+ public byte[] readBytes() throws IOException
+ {
+ return mH2i.readBytes();
+ }
+
+ public String readUTF() throws IOException
+ {
+ return mH2i.readString();
+ }
+
+ public Object readObject() throws IOException
+ {
+ return mH2i.readObject();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(Class<T> cls) throws IOException,
+ ClassNotFoundException {
+ return (T) mH2i.readObject(cls);
+ }
+
+ public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException
+ {
+ return readObject(cls);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectOutput.java
new file mode 100644
index 0000000..1f3c57d
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectOutput.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.hessian;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.alibaba.com.caucho.hessian.io.Hessian2Output;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * Hessian2 Object output.
+ *
+ * @author qian.lei
+ */
+
+public class Hessian2ObjectOutput implements ObjectOutput
+{
+ private final Hessian2Output mH2o;
+
+ public Hessian2ObjectOutput(OutputStream os)
+ {
+ mH2o = new Hessian2Output(os);
+ mH2o.setSerializerFactory(Hessian2SerializerFactory.SERIALIZER_FACTORY);
+ }
+
+ public void writeBool(boolean v) throws IOException
+ {
+ mH2o.writeBoolean(v);
+ }
+
+ public void writeByte(byte v) throws IOException
+ {
+ mH2o.writeInt(v);
+ }
+
+ public void writeShort(short v) throws IOException
+ {
+ mH2o.writeInt(v);
+ }
+
+ public void writeInt(int v) throws IOException
+ {
+ mH2o.writeInt(v);
+ }
+
+ public void writeLong(long v) throws IOException
+ {
+ mH2o.writeLong(v);
+ }
+
+ public void writeFloat(float v) throws IOException
+ {
+ mH2o.writeDouble(v);
+ }
+
+ public void writeDouble(double v) throws IOException
+ {
+ mH2o.writeDouble(v);
+ }
+
+ public void writeBytes(byte[] b) throws IOException
+ {
+ mH2o.writeBytes(b);
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException
+ {
+ mH2o.writeBytes(b, off, len);
+ }
+
+ public void writeUTF(String v) throws IOException
+ {
+ mH2o.writeString(v);
+ }
+
+ public void writeObject(Object obj) throws IOException
+ {
+ mH2o.writeObject(obj);
+ }
+
+ public void flushBuffer() throws IOException
+ {
+ mH2o.flushBuffer();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.java
new file mode 100644
index 0000000..fa61b1b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.hessian;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+public class Hessian2Serialization implements Serialization {
+
+ public static final byte ID = 2;
+
+ public byte getContentTypeId() {
+ return ID;
+ }
+
+ public String getContentType() {
+ return "x-application/hessian2";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
+ return new Hessian2ObjectOutput(out);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream is) throws IOException {
+ return new Hessian2ObjectInput(is);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2SerializerFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2SerializerFactory.java
new file mode 100644
index 0000000..5289be0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2SerializerFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.hessian;
+
+import com.alibaba.com.caucho.hessian.io.SerializerFactory;
+
+public class Hessian2SerializerFactory extends SerializerFactory {
+
+ public static final SerializerFactory SERIALIZER_FACTORY = new Hessian2SerializerFactory();
+
+ private Hessian2SerializerFactory() {
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.java
new file mode 100644
index 0000000..7b6fd26
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+public class CompactedJavaSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 4;
+ }
+
+ public String getContentType() {
+ return "x-application/compactedjava";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
+ return new JavaObjectOutput(out, true);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream is) throws IOException {
+ return new JavaObjectInput(is, true);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectInputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectInputStream.java
new file mode 100644
index 0000000..34068f6
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectInputStream.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.io.StreamCorruptedException;
+
+import com.alibaba.dubbo.common.utils.ClassHelper;
+
+/**
+ * Compacted java object input stream.
+ *
+ * @author qianlei
+ */
+
+public class CompactedObjectInputStream extends ObjectInputStream
+{
+ private ClassLoader mClassLoader;
+
+ public CompactedObjectInputStream(InputStream in) throws IOException
+ {
+ this(in, Thread.currentThread().getContextClassLoader());
+ }
+
+ public CompactedObjectInputStream(InputStream in, ClassLoader cl) throws IOException
+ {
+ super(in);
+ mClassLoader = cl == null ? ClassHelper.getClassLoader() : cl;
+ }
+
+ @Override
+ protected ObjectStreamClass readClassDescriptor() throws IOException,ClassNotFoundException
+ {
+ int type = read();
+ if( type < 0 )
+ throw new EOFException();
+ switch( type )
+ {
+ case 0:
+ return super.readClassDescriptor();
+ case 1:
+ Class<?> clazz = loadClass(readUTF());
+ return ObjectStreamClass.lookup(clazz);
+ default:
+ throw new StreamCorruptedException("Unexpected class descriptor type: " + type);
+ }
+ }
+
+ private Class<?> loadClass(String className) throws ClassNotFoundException
+ {
+ return mClassLoader.loadClass(className);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectOutputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectOutputStream.java
new file mode 100644
index 0000000..716cc21
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectOutputStream.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
+
+/**
+ * Compacted java object output stream.
+ *
+ * @author qianlei
+ */
+
+public class CompactedObjectOutputStream extends ObjectOutputStream
+{
+ public CompactedObjectOutputStream(OutputStream out) throws IOException
+ {
+ super(out);
+ }
+
+ @Override
+ protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException
+ {
+ Class<?> clazz = desc.forClass();
+ if( clazz.isPrimitive() || clazz.isArray() )
+ {
+ write(0);
+ super.writeClassDescriptor(desc);
+ }
+ else
+ {
+ write(1);
+ writeUTF(desc.getName());
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectInput.java
new file mode 100644
index 0000000..e79d3b1
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectInput.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.lang.reflect.Type;
+
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+
+/**
+ * Java Object input.
+ *
+ * @author qian.lei
+ */
+
+public class JavaObjectInput implements ObjectInput
+{
+ public final static int MAX_BYTE_ARRAY_LENGTH = 8 * 1024 * 1024;
+
+ private final ObjectInputStream mIn;
+
+ public JavaObjectInput(InputStream is) throws IOException
+ {
+ mIn = new ObjectInputStream(is);
+ }
+
+ public JavaObjectInput(InputStream is, boolean compacted) throws IOException
+ {
+ mIn = compacted ? new CompactedObjectInputStream(is) : new ObjectInputStream(is);
+ }
+
+ public boolean readBool() throws IOException
+ {
+ return mIn.readBoolean();
+ }
+
+ public byte readByte() throws IOException
+ {
+ return mIn.readByte();
+ }
+
+ public short readShort() throws IOException
+ {
+ return mIn.readShort();
+ }
+
+ public int readInt() throws IOException
+ {
+ return mIn.readInt();
+ }
+
+ public long readLong() throws IOException
+ {
+ return mIn.readLong();
+ }
+
+ public float readFloat() throws IOException
+ {
+ return mIn.readFloat();
+ }
+
+ public double readDouble() throws IOException
+ {
+ return mIn.readDouble();
+ }
+
+ public byte[] readBytes() throws IOException
+ {
+ int len = mIn.readInt();
+ if( len < 0 )
+ return null;
+ if( len == 0 )
+ return new byte[0];
+ if( len > MAX_BYTE_ARRAY_LENGTH )
+ throw new IOException("Byte array length too large. " + len);
+
+ byte[] b = new byte[len];
+ mIn.readFully(b);
+ return b;
+ }
+
+ public String readUTF() throws IOException
+ {
+ int len = mIn.readInt();
+ if( len < 0 )
+ return null;
+
+ return mIn.readUTF();
+ }
+
+ public Object readObject() throws IOException, ClassNotFoundException
+ {
+ byte b = mIn.readByte();
+ if( b == 0 )
+ return null;
+
+ return mIn.readObject();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(Class<T> cls) throws IOException,
+ ClassNotFoundException {
+ return (T) readObject();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException
+ {
+ return (T) readObject();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectOutput.java
new file mode 100644
index 0000000..f12e031
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectOutput.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * Java Object output.
+ *
+ * @author qian.lei
+ */
+
+public class JavaObjectOutput implements ObjectOutput
+{
+ private final ObjectOutputStream mOut;
+
+ public JavaObjectOutput(OutputStream os) throws IOException
+ {
+ mOut = new ObjectOutputStream(os);
+ }
+
+ public JavaObjectOutput(OutputStream os, boolean compact) throws IOException
+ {
+ mOut = compact ? new CompactedObjectOutputStream(os) : new ObjectOutputStream(os);
+ }
+
+ public void writeBool(boolean v) throws IOException
+ {
+ mOut.writeBoolean(v);
+ }
+
+ public void writeByte(byte v) throws IOException
+ {
+ mOut.writeByte(v);
+ }
+
+ public void writeShort(short v) throws IOException
+ {
+ mOut.writeShort(v);
+ }
+
+ public void writeInt(int v) throws IOException
+ {
+ mOut.writeInt(v);
+ }
+
+ public void writeLong(long v) throws IOException
+ {
+ mOut.writeLong(v);
+ }
+
+ public void writeFloat(float v) throws IOException
+ {
+ mOut.writeFloat(v);
+ }
+
+ public void writeDouble(double v) throws IOException
+ {
+ mOut.writeDouble(v);
+ }
+
+ public void writeBytes(byte[] b) throws IOException
+ {
+ if( b == null )
+ mOut.writeInt(-1);
+ else
+ this.writeBytes(b, 0, b.length);
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException
+ {
+ mOut.writeInt(len);
+ mOut.write(b, off, len);
+ }
+
+ public void writeUTF(String v) throws IOException
+ {
+ if( v == null )
+ {
+ mOut.writeInt(-1);
+ }
+ else
+ {
+ mOut.writeInt(v.length());
+ mOut.writeUTF(v);
+ }
+ }
+
+ public void writeObject(Object obj) throws IOException
+ {
+ if( obj == null )
+ {
+ mOut.writeByte(0);
+ }
+ else
+ {
+ mOut.writeByte(1);
+ mOut.writeObject(obj);
+ }
+ }
+
+ public void flushBuffer() throws IOException
+ {
+ mOut.flush();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.java
new file mode 100644
index 0000000..e3b6d4b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+public class JavaSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 3;
+ }
+
+ public String getContentType() {
+ return "x-application/java";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
+ return new JavaObjectOutput(out);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream is) throws IOException {
+ return new JavaObjectInput(is);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectInput.java
new file mode 100644
index 0000000..bf38939
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectInput.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.lang.reflect.Type;
+
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.fastjson.JSON;
+
+/**
+ * JsonObjectInput
+ *
+ * @author william.liangf
+ */
+public class FastJsonObjectInput implements ObjectInput {
+
+ private final BufferedReader reader;
+
+ public FastJsonObjectInput(InputStream in){
+ this(new InputStreamReader(in));
+ }
+
+ public FastJsonObjectInput(Reader reader){
+ this.reader = new BufferedReader(reader);
+ }
+
+ public boolean readBool() throws IOException {
+ try {
+ return readObject(boolean.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public byte readByte() throws IOException {
+ try {
+ return readObject( byte.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public short readShort() throws IOException {
+ try {
+ return readObject(short.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public int readInt() throws IOException {
+ try {
+ return readObject(int.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public long readLong() throws IOException {
+ try {
+ return readObject(long.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public float readFloat() throws IOException {
+ try {
+ return readObject(float.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public double readDouble() throws IOException {
+ try {
+ return readObject(double.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public String readUTF() throws IOException {
+ try {
+ return readObject(String.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public byte[] readBytes() throws IOException {
+ return readLine().getBytes();
+ }
+
+ public Object readObject() throws IOException, ClassNotFoundException {
+ String json = readLine();
+ return JSON.parse(json);
+ }
+
+ public <T> T readObject(Class<T> cls) throws IOException, ClassNotFoundException {
+ String json = readLine();
+ return JSON.parseObject(json, cls);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException
+ {
+ Object value = readObject(cls);
+ return (T) PojoUtils.realize(value, cls, type);
+ }
+
+ private String readLine() throws IOException, EOFException {
+ String line = reader.readLine();
+ if(line == null || line.trim().length() == 0) throw new EOFException();
+ return line;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectOutput.java
new file mode 100644
index 0000000..ffaaf97
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectOutput.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.fastjson.serializer.JSONSerializer;
+import com.alibaba.fastjson.serializer.SerializeWriter;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+
+/**
+ * JsonObjectOutput
+ *
+ * @author william.liangf
+ */
+public class FastJsonObjectOutput implements ObjectOutput {
+
+ private final PrintWriter writer;
+
+ public FastJsonObjectOutput(OutputStream out) {
+ this(new OutputStreamWriter(out));
+ }
+
+ public FastJsonObjectOutput(Writer writer) {
+ this.writer = new PrintWriter(writer);
+ }
+
+ public void writeBool(boolean v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeByte(byte v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeShort(short v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeInt(int v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeLong(long v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeFloat(float v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeDouble(double v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeUTF(String v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeBytes(byte[] b) throws IOException {
+ writer.println(new String(b));
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException {
+ writer.println(new String(b, off, len));
+ }
+
+ public void writeObject(Object obj) throws IOException {
+ SerializeWriter out = new SerializeWriter();
+ JSONSerializer serializer = new JSONSerializer(out);
+ serializer.config(SerializerFeature.WriteEnumUsingToString, true);
+ serializer.write(obj);
+ out.writeTo(writer);
+ writer.println();
+ writer.flush();
+ }
+
+ public void flushBuffer() throws IOException {
+ writer.flush();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.java
new file mode 100644
index 0000000..28c00e6
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * FastJsonSerialization
+ *
+ * @author william.liangf
+ */
+public class FastJsonSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 6;
+ }
+
+ public String getContentType() {
+ return "text/json";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream output) throws IOException {
+ return new FastJsonObjectOutput(output);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream input) throws IOException {
+ return new FastJsonObjectInput(input);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectInput.java
new file mode 100644
index 0000000..d6724da
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectInput.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.json.ParseException;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+
+/**
+ * JsonObjectInput
+ *
+ * @author william.liangf
+ * @author ding.lid
+ */
+public class JsonObjectInput implements ObjectInput {
+
+ private final BufferedReader reader;
+
+ public JsonObjectInput(InputStream in){
+ this(new InputStreamReader(in));
+ }
+
+ public JsonObjectInput(Reader reader){
+ this.reader = new BufferedReader(reader);
+ }
+
+ public boolean readBool() throws IOException {
+ try {
+ return readObject(boolean.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public byte readByte() throws IOException {
+ try {
+ return readObject( byte.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public short readShort() throws IOException {
+ try {
+ return readObject(short.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public int readInt() throws IOException {
+ try {
+ return readObject(int.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public long readLong() throws IOException {
+ try {
+ return readObject(long.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public float readFloat() throws IOException {
+ try {
+ return readObject(float.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public double readDouble() throws IOException {
+ try {
+ return readObject(double.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public String readUTF() throws IOException {
+ try {
+ return readObject(String.class);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ public byte[] readBytes() throws IOException {
+ return readLine().getBytes();
+ }
+
+ public Object readObject() throws IOException, ClassNotFoundException {
+ try {
+ String json = readLine();
+ if (json.startsWith("{")) {
+ return JSON.parse(json, Map.class);
+ } else {
+ json = "{\"value\":" + json + "}";
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> map = JSON.parse(json, Map.class);
+ return map.get("value");
+ }
+ } catch (ParseException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(Class<T> cls) throws IOException, ClassNotFoundException {
+ Object value = readObject();
+ return (T) PojoUtils.realize(value, cls);
+ /*try {
+ return JSON.parse(readLine(), cls);
+ } catch (ParseException e) {
+ throw new IOException(e.getMessage());
+ }*/
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException
+ {
+ Object value = readObject();
+ return (T) PojoUtils.realize(value, cls, type);
+ }
+
+ private String readLine() throws IOException, EOFException {
+ String line = reader.readLine();
+ if(line == null || line.trim().length() == 0) throw new EOFException();
+ return line;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectOutput.java
new file mode 100644
index 0000000..53ebc2f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectOutput.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * JsonObjectOutput
+ *
+ * @author william.liangf
+ */
+public class JsonObjectOutput implements ObjectOutput {
+
+ private final PrintWriter writer;
+
+ private final boolean writeClass;
+
+ public JsonObjectOutput(OutputStream out) {
+ this(new OutputStreamWriter(out), false);
+ }
+
+ public JsonObjectOutput(Writer writer) {
+ this(writer, false);
+ }
+
+ public JsonObjectOutput(OutputStream out, boolean writeClass) {
+ this(new OutputStreamWriter(out), writeClass);
+ }
+
+ public JsonObjectOutput(Writer writer, boolean writeClass) {
+ this.writer = new PrintWriter(writer);
+ this.writeClass = writeClass;
+ }
+
+ public void writeBool(boolean v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeByte(byte v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeShort(short v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeInt(int v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeLong(long v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeFloat(float v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeDouble(double v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeUTF(String v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeBytes(byte[] b) throws IOException {
+ writer.println(new String(b));
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException {
+ writer.println(new String(b, off, len));
+ }
+
+ public void writeObject(Object obj) throws IOException {
+ JSON.json(obj, writer, writeClass);
+ writer.println();
+ writer.flush();
+ }
+
+ public void flushBuffer() throws IOException {
+ writer.flush();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.java
new file mode 100644
index 0000000..0628fdd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * JsonSerialization
+ *
+ * @author william.liangf
+ */
+public class JsonSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 5;
+ }
+
+ public String getContentType() {
+ return "text/json";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream output) throws IOException {
+ return new JsonObjectOutput(output, url.getParameter("with.class", true));
+ }
+
+ public ObjectInput deserialize(URL url, InputStream input) throws IOException {
+ return new JsonObjectInput(input);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/Status.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/Status.java
new file mode 100644
index 0000000..1029f6f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/Status.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.status;
+
+/**
+ * Status
+ *
+ * @author william.liangf
+ */
+public class Status {
+
+ /**
+ * Level
+ */
+ public static enum Level {
+ /**
+ * OK
+ */
+ OK,
+
+ /**
+ * WARN
+ */
+ WARN,
+
+ /**
+ * ERROR
+ */
+ ERROR,
+
+ /**
+ * UNKNOWN
+ */
+ UNKNOWN
+ }
+
+ private final Level level;
+
+ private final String message;
+
+ private final String description;
+
+ public Status(Level level){
+ this(level, null, null);
+ }
+
+ public Status(Level level, String message){
+ this(level, message, null);
+ }
+
+ public Status(Level level, String message, String description){
+ this.level = level;
+ this.message = message;
+ this.description = description;
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java
new file mode 100644
index 0000000..8a95616
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.status;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * StatusChecker
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface StatusChecker {
+
+ /**
+ * check status
+ *
+ * @return status
+ */
+ Status check();
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/LoadStatusChecker.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/LoadStatusChecker.java
new file mode 100644
index 0000000..3b75774
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/LoadStatusChecker.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.status.support;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+
+/**
+ * Load Status
+ *
+ * @author william.liangf
+ */
+@Activate
+public class LoadStatusChecker implements StatusChecker {
+
+ public Status check() {
+ OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
+ double load;
+ try {
+ Method method = OperatingSystemMXBean.class.getMethod("getSystemLoadAverage", new Class<?>[0]);
+ load = (Double)method.invoke(operatingSystemMXBean, new Object[0]);
+ } catch (Throwable e) {
+ load = -1;
+ }
+ int cpu = operatingSystemMXBean.getAvailableProcessors();
+ return new Status(load < 0 ? Status.Level.UNKNOWN : (load < cpu ? Status.Level.OK : Status.Level.WARN), (load < 0 ? "" : "load:" + load + ",") + "cpu:" + cpu);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/MemoryStatusChecker.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/MemoryStatusChecker.java
new file mode 100644
index 0000000..cf3ecea
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/MemoryStatusChecker.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.status.support;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+
+/**
+ * MemoryStatus
+ *
+ * @author william.liangf
+ */
+@Activate
+public class MemoryStatusChecker implements StatusChecker {
+
+ public Status check() {
+ Runtime runtime = Runtime.getRuntime();
+ long freeMemory = runtime.freeMemory();
+ long totalMemory = runtime.totalMemory();
+ long maxMemory = runtime.maxMemory();
+ boolean ok = (maxMemory - (totalMemory - freeMemory) > 2048); // 剩余空间小于2M报警
+ String msg = "max:" + (maxMemory / 1024 / 1024) + "M,total:"
+ + (totalMemory / 1024 / 1024) + "M,used:" + ((totalMemory / 1024 / 1024) - (freeMemory / 1024 / 1024)) + "M,free:" + (freeMemory / 1024 / 1024) + "M";
+ return new Status(ok ? Status.Level.OK : Status.Level.WARN, msg);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/StatusUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/StatusUtils.java
new file mode 100644
index 0000000..f909499
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/StatusUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.status.support;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.Status.Level;
+
+/**
+ * StatusManager
+ *
+ * @author william.liangf
+ */
+public class StatusUtils {
+
+ public static Status getSummaryStatus(Map<String, Status> statuses) {
+ Level level = Level.OK;
+ StringBuilder msg = new StringBuilder();
+ for (Map.Entry<String, Status> entry : statuses.entrySet()) {
+ String key = entry.getKey();
+ Status status = entry.getValue();
+ Level l = status.getLevel();
+ if (Level.ERROR.equals(l)) {
+ level = Level.ERROR;
+ if (msg.length() > 0) {
+ msg.append(",");
+ }
+ msg.append(key);
+ } else if (Level.WARN.equals(l)) {
+ if(! Level.ERROR.equals(level)) {
+ level = Level.WARN;
+ }
+ if (msg.length() > 0) {
+ msg.append(",");
+ }
+ msg.append(key);
+ }
+ }
+ return new Status(level, msg.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/ThreadPool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/ThreadPool.java
new file mode 100644
index 0000000..57f50a2
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/ThreadPool.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.threadpool;
+
+import java.util.concurrent.Executor;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * ThreadPool
+ *
+ * @author william.liangf
+ */
+@SPI("fixed")
+public interface ThreadPool {
+
+ /**
+ * 线程池
+ *
+ * @param url 线程参数
+ * @return 线程池
+ */
+ @Adaptive({Constants.THREADPOOL_KEY})
+ Executor getExecutor(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/AbortPolicyWithReport.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/AbortPolicyWithReport.java
new file mode 100644
index 0000000..4296e5e
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/AbortPolicyWithReport.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.threadpool.support;
+
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * Abort Policy.
+ * Log warn info when abort.
+ *
+ * @author ding.lid
+ */
+public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {
+
+ protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class);
+
+ private final String threadName;
+
+ private final URL url;
+
+ public AbortPolicyWithReport(String threadName, URL url) {
+ this.threadName = threadName;
+ this.url = url;
+ }
+
+ @Override
+ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
+ String msg = String.format("Thread pool is EXHAUSTED!" +
+ " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d), Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s!" ,
+ threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),
+ e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(), url.toIdentityString());
+ logger.warn(msg);
+ throw new RejectedExecutionException(msg);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.java
new file mode 100644
index 0000000..5d9cdc0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.threadpool.support.cached;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.threadpool.ThreadPool;
+import com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+
+/**
+ * CachedThreadPool
+ *
+ * @see java.util.concurrent.Executors#newCachedThreadPool()
+ * @author william.liangf
+ */
+public class CachedThreadPool implements ThreadPool {
+
+ public Executor getExecutor(URL url) {
+ String threadName = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
+ int threads = url.getParameter(Constants.THREADS_KEY, Integer.MAX_VALUE);
+ int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
+ int keepalive = url.getParameter(Constants.THREAD_ALIVE_KEY, Constants.DEFAULT_THREAD_ALIVE);
+ return new ThreadPoolExecutor(0, threads, keepalive, TimeUnit.MILLISECONDS,
+ queues <= 0 ? new SynchronousQueue<Runnable>() : new LinkedBlockingQueue<Runnable>(queues),
+ new NamedThreadFactory(threadName, true), new AbortPolicyWithReport(threadName, url));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.java
new file mode 100644
index 0000000..4ed95c7
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.threadpool.support.fixed;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.threadpool.ThreadPool;
+import com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+
+/**
+ * FixedThreadPool
+ *
+ * @see java.util.concurrent.Executors#newFixedThreadPool(int)
+ * @author william.liangf
+ */
+public class FixedThreadPool implements ThreadPool {
+
+ public Executor getExecutor(URL url) {
+ String threadName = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
+ int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
+ int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
+ return new ThreadPoolExecutor(threads, threads, Constants.DEFAULT_THREAD_ALIVE, TimeUnit.MILLISECONDS,
+ queues <= 0 ? new SynchronousQueue<Runnable>() : new LinkedBlockingQueue<Runnable>(queues),
+ new NamedThreadFactory(threadName, true), new AbortPolicyWithReport(threadName, url));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/AtomicPositiveInteger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/AtomicPositiveInteger.java
new file mode 100644
index 0000000..b1ade56
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/AtomicPositiveInteger.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * AtomicPositiveInteger
+ *
+ * @author william.liangf
+ * @author ding.lid
+ */
+public class AtomicPositiveInteger extends Number {
+
+ private static final long serialVersionUID = -3038533876489105940L;
+
+ private final AtomicInteger i;
+
+ public AtomicPositiveInteger() {
+ i = new AtomicInteger();
+ }
+
+ public AtomicPositiveInteger(int initialValue) {
+ i = new AtomicInteger(initialValue);
+ }
+
+ public final int getAndIncrement() {
+ for (;;) {
+ int current = i.get();
+ int next = (current >= Integer.MAX_VALUE ? 0 : current + 1);
+ if (i.compareAndSet(current, next)) {
+ return current;
+ }
+ }
+ }
+
+ public final int getAndDecrement() {
+ for (;;) {
+ int current = i.get();
+ int next = (current <= 0 ? Integer.MAX_VALUE : current - 1);
+ if (i.compareAndSet(current, next)) {
+ return current;
+ }
+ }
+ }
+
+ public final int incrementAndGet() {
+ for (;;) {
+ int current = i.get();
+ int next = (current >= Integer.MAX_VALUE ? 0 : current + 1);
+ if (i.compareAndSet(current, next)) {
+ return next;
+ }
+ }
+ }
+
+ public final int decrementAndGet() {
+ for (;;) {
+ int current = i.get();
+ int next = (current <= 0 ? Integer.MAX_VALUE : current - 1);
+ if (i.compareAndSet(current, next)) {
+ return next;
+ }
+ }
+ }
+
+ public final int get() {
+ return i.get();
+ }
+
+ public final void set(int newValue) {
+ if (newValue < 0) {
+ throw new IllegalArgumentException("new value " + newValue + " < 0");
+ }
+ i.set(newValue);
+ }
+
+ public final int getAndSet(int newValue) {
+ if (newValue < 0) {
+ throw new IllegalArgumentException("new value " + newValue + " < 0");
+ }
+ return i.getAndSet(newValue);
+ }
+
+ public final int getAndAdd(int delta) {
+ if (delta < 0) {
+ throw new IllegalArgumentException("delta " + delta + " < 0");
+ }
+ for (;;) {
+ int current = i.get();
+ int next = (current >= Integer.MAX_VALUE - delta + 1 ? delta - 1 : current + delta);
+ if (i.compareAndSet(current, next)) {
+ return current;
+ }
+ }
+ }
+
+ public final int addAndGet(int delta) {
+ if (delta < 0) {
+ throw new IllegalArgumentException("delta " + delta + " < 0");
+ }
+ for (;;) {
+ int current = i.get();
+ int next = (current >= Integer.MAX_VALUE - delta + 1 ? delta - 1 : current + delta);
+ if (i.compareAndSet(current, next)) {
+ return next;
+ }
+ }
+ }
+
+ public final boolean compareAndSet(int expect, int update) {
+ if (update < 0) {
+ throw new IllegalArgumentException("update value " + update + " < 0");
+ }
+ return i.compareAndSet(expect, update);
+ }
+
+ public final boolean weakCompareAndSet(int expect, int update) {
+ if (update < 0) {
+ throw new IllegalArgumentException("update value " + update + " < 0");
+ }
+ return i.weakCompareAndSet(expect, update);
+ }
+
+ public byte byteValue() {
+ return i.byteValue();
+ }
+
+ public short shortValue() {
+ return i.shortValue();
+ }
+
+ public int intValue() {
+ return i.intValue();
+ }
+
+ public long longValue() {
+ return i.longValue();
+ }
+
+ public float floatValue() {
+ return i.floatValue();
+ }
+
+ public double doubleValue() {
+ return i.doubleValue();
+ }
+
+ public String toString() {
+ return i.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((i == null) ? 0 : i.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ AtomicPositiveInteger other = (AtomicPositiveInteger) obj;
+ if (i == null) {
+ if (other.i != null) return false;
+ } else if (!i.equals(other.i)) return false;
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ClassHelper.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ClassHelper.java
new file mode 100644
index 0000000..0c66edd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ClassHelper.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class ClassHelper {
+
+ /**
+ * get class loader
+ *
+ * @param cls
+ * @return class loader
+ */
+ public static ClassLoader getClassLoader(Class<?> cls) {
+ ClassLoader cl = null;
+ try {
+ cl = Thread.currentThread().getContextClassLoader();
+ } catch (Throwable ex) {
+ // Cannot access thread context ClassLoader - falling back to system class loader...
+ }
+ if (cl == null) {
+ // No thread context class loader -> use class loader of this class.
+ cl = cls.getClassLoader();
+ }
+ return cl;
+ }
+
+ /**
+ * Return the default ClassLoader to use: typically the thread context
+ * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
+ * class will be used as fallback.
+ * <p>
+ * Call this method if you intend to use the thread context ClassLoader in a
+ * scenario where you absolutely need a non-null ClassLoader reference: for
+ * example, for class path resource loading (but not necessarily for
+ * <code>Class.forName</code>, which accepts a <code>null</code> ClassLoader
+ * reference as well).
+ *
+ * @return the default ClassLoader (never <code>null</code>)
+ * @see java.lang.Thread#getContextClassLoader()
+ */
+ public static ClassLoader getClassLoader() {
+ return getClassLoader(ClassHelper.class);
+ }
+
+ /**
+ * Same as <code>Class.forName()</code>, except that it works for primitive
+ * types.
+ */
+ public static Class<?> forName(String name) throws ClassNotFoundException {
+ return forName(name, getClassLoader());
+ }
+
+ /**
+ * Replacement for <code>Class.forName()</code> that also returns Class
+ * instances for primitives (like "int") and array class names (like
+ * "String[]").
+ *
+ * @param name the name of the Class
+ * @param classLoader the class loader to use (may be <code>null</code>,
+ * which indicates the default class loader)
+ * @return Class instance for the supplied name
+ * @throws ClassNotFoundException if the class was not found
+ * @throws LinkageError if the class file could not be loaded
+ * @see Class#forName(String, boolean, ClassLoader)
+ */
+ public static Class<?> forName(String name, ClassLoader classLoader)
+ throws ClassNotFoundException, LinkageError {
+
+ Class<?> clazz = resolvePrimitiveClassName(name);
+ if (clazz != null) {
+ return clazz;
+ }
+
+ // "java.lang.String[]" style arrays
+ if (name.endsWith(ARRAY_SUFFIX)) {
+ String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
+ Class<?> elementClass = forName(elementClassName, classLoader);
+ return Array.newInstance(elementClass, 0).getClass();
+ }
+
+ // "[Ljava.lang.String;" style arrays
+ int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX);
+ if (internalArrayMarker != -1 && name.endsWith(";")) {
+ String elementClassName = null;
+ if (internalArrayMarker == 0) {
+ elementClassName = name
+ .substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1);
+ } else if (name.startsWith("[")) {
+ elementClassName = name.substring(1);
+ }
+ Class<?> elementClass = forName(elementClassName, classLoader);
+ return Array.newInstance(elementClass, 0).getClass();
+ }
+
+ ClassLoader classLoaderToUse = classLoader;
+ if (classLoaderToUse == null) {
+ classLoaderToUse = getClassLoader();
+ }
+ return classLoaderToUse.loadClass(name);
+ }
+
+ /**
+ * Resolve the given class name as primitive class, if appropriate,
+ * according to the JVM's naming rules for primitive classes.
+ * <p>
+ * Also supports the JVM's internal class names for primitive arrays. Does
+ * <i>not</i> support the "[]" suffix notation for primitive arrays; this is
+ * only supported by {@link #forName}.
+ *
+ * @param name the name of the potentially primitive class
+ * @return the primitive class, or <code>null</code> if the name does not
+ * denote a primitive class or primitive array class
+ */
+ public static Class<?> resolvePrimitiveClassName(String name) {
+ Class<?> result = null;
+ // Most class names will be quite long, considering that they
+ // SHOULD sit in a package, so a length check is worthwhile.
+ if (name != null && name.length() <= 8) {
+ // Could be a primitive - likely.
+ result = (Class<?>) primitiveTypeNameMap.get(name);
+ }
+ return result;
+ }
+
+ /** Suffix for array class names: "[]" */
+ public static final String ARRAY_SUFFIX = "[]";
+ /** Prefix for internal array class names: "[L" */
+ private static final String INTERNAL_ARRAY_PREFIX = "[L";
+
+ /**
+ * Map with primitive type name as key and corresponding primitive type as
+ * value, for example: "int" -> "int.class".
+ */
+ private static final Map<String,Class<?>> primitiveTypeNameMap = new HashMap<String, Class<?>>(16);
+
+ /**
+ * Map with primitive wrapper type as key and corresponding primitive type
+ * as value, for example: Integer.class -> int.class.
+ */
+ private static final Map<Class<?>,Class<?>> primitiveWrapperTypeMap = new HashMap<Class<?>, Class<?>>(8);
+
+ static {
+ primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
+ primitiveWrapperTypeMap.put(Byte.class, byte.class);
+ primitiveWrapperTypeMap.put(Character.class, char.class);
+ primitiveWrapperTypeMap.put(Double.class, double.class);
+ primitiveWrapperTypeMap.put(Float.class, float.class);
+ primitiveWrapperTypeMap.put(Integer.class, int.class);
+ primitiveWrapperTypeMap.put(Long.class, long.class);
+ primitiveWrapperTypeMap.put(Short.class, short.class);
+
+ Set<Class<?>> primitiveTypeNames = new HashSet<Class<?>>(16);
+ primitiveTypeNames.addAll(primitiveWrapperTypeMap.values());
+ primitiveTypeNames.addAll(Arrays
+ .asList(new Class<?>[] { boolean[].class, byte[].class, char[].class, double[].class,
+ float[].class, int[].class, long[].class, short[].class }));
+ for (Iterator<Class<?>> it = primitiveTypeNames.iterator(); it.hasNext();) {
+ Class<?> primitiveClass = (Class<?>) it.next();
+ primitiveTypeNameMap.put(primitiveClass.getName(), primitiveClass);
+ }
+ }
+
+ public static String toShortString(Object obj){
+ if(obj == null){
+ return "null";
+ }
+ return obj.getClass().getSimpleName() + "@" + System.identityHashCode(obj);
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CollectionUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CollectionUtils.java
new file mode 100644
index 0000000..8f89202
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CollectionUtils.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.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+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 String join(List<String> list, String separator) {
+ StringBuilder sb = new StringBuilder();
+ for(String ele : list) {
+ if(sb.length() > 0) {
+ sb.append(separator);
+ }
+ sb.append(ele);
+ }
+ return sb.toString();
+ }
+
+ 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;
+ }
+
+ public static boolean isEmpty(Collection<?> collection) {
+ return collection == null || collection.size() == 0;
+ }
+
+ public static boolean isNotEmpty(Collection<?> collection) {
+ return collection != null && collection.size() > 0;
+ }
+
+ 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..c78b251
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtils.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.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 == Short.class || type == short.class) {
+ return new Short(string);
+ } else if(type == Integer.class || type == int.class) {
+ return new Integer(string);
+ } else if(type == Long.class || type == long.class) {
+ return new Long(string);
+ } else if(type == Double.class || type == double.class) {
+ return new Double(string);
+ } else if(type == Float.class || type == float.class) {
+ return new Float(string);
+ } else if(type == Byte.class || type == byte.class) {
+ return new Byte(string);
+ } else if(type == Boolean.class || type == boolean.class) {
+ return new Boolean(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 (type == Class.class) {
+ try {
+ return ReflectUtils.name2class((String)value);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(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 ? e==null : 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 ? e2==null : 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 ? e==null : 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..0d579c75
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ConfigUtils.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * @author ding.lid
+ * @author william.liangf
+ */
+public class ConfigUtils {
+
+ private static final Logger logger = LoggerFactory.getLogger(ConfigUtils.class);
+
+ public static boolean isNotEmpty(String value) {
+ return ! isEmpty(value);
+ }
+
+ public static boolean isEmpty(String value) {
+ return value == null || value.length() == 0
+ || "false".equalsIgnoreCase(value)
+ || "0".equalsIgnoreCase(value)
+ || "null".equalsIgnoreCase(value)
+ || "N/A".equalsIgnoreCase(value);
+ }
+
+ public static boolean isDefault(String value) {
+ return "true".equalsIgnoreCase(value)
+ || "default".equalsIgnoreCase(value);
+ }
+
+ /**
+ * 扩展点列表中插入缺省扩展点。
+ * <p>
+ * 扩展点列表支持<ul>
+ * <li>特殊值<code><strong>default</strong></code>,表示缺省扩展点插入的位置
+ * <li>特殊符号<code><strong>-</strong></code>,表示剔除。 <code>-foo1</code>,剔除添加缺省扩展点foo1。<code>-default</code>,剔除添加所有缺省扩展点。
+ * </ul>
+ *
+ * @param type 扩展点类型
+ * @param cfg 扩展点名列表
+ * @param def 缺省的扩展点的列表
+ * @return 完成缺省的扩展点列表插入后的列表
+ */
+ public static List<String> mergeValues(Class<?> type, String cfg, List<String> def) {
+ List<String> defaults = new ArrayList<String>();
+ if (def != null) {
+ for (String name : def) {
+ if (ExtensionLoader.getExtensionLoader(type).hasExtension(name)) {
+ defaults.add(name);
+ }
+ }
+ }
+
+ List<String> names = new ArrayList<String>();
+
+ // 加入初始值
+ String[] configs = (cfg == null || cfg.trim().length() == 0) ? new String[0] : Constants.COMMA_SPLIT_PATTERN.split(cfg);
+ for (String config : configs) {
+ if(config != null && config.trim().length() > 0) {
+ names.add(config);
+ }
+ }
+
+ // 不包含 -default
+ if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
+ // 加入 插入缺省扩展点
+ int i = names.indexOf(Constants.DEFAULT_KEY);
+ if (i > 0) {
+ names.addAll(i, defaults);
+ } else {
+ names.addAll(0, defaults);
+ }
+ names.remove(Constants.DEFAULT_KEY);
+ }
+ else {
+ names.remove(Constants.DEFAULT_KEY);
+ }
+
+ // 合并-的配置项
+ for (String name : new ArrayList<String>(names)) {
+ if (name.startsWith(Constants.REMOVE_VALUE_PREFIX)) {
+ names.remove(name);
+ names.remove(name.substring(1));
+ }
+ }
+ return names;
+ }
+
+ private static Pattern VARIABLE_PATTERN = Pattern.compile(
+ "\\$\\s*\\{?\\s*([\\._0-9a-zA-Z]+)\\s*\\}?");
+
+ public static String replaceProperty(String expression, Map<String, String> params) {
+ if (expression == null || expression.length() == 0 || expression.indexOf('$') < 0) {
+ return expression;
+ }
+ Matcher matcher = VARIABLE_PATTERN.matcher(expression);
+ StringBuffer sb = new StringBuffer();
+ while (matcher.find()) { // 逐个匹配
+ String key = matcher.group(1);
+ String value = System.getProperty(key);
+ if (value == null && params != null) {
+ value = params.get(key);
+ }
+ if (value == null) {
+ value = "";
+ }
+ matcher.appendReplacement(sb, Matcher.quoteReplacement(value));
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+
+ private static volatile Properties PROPERTIES;
+
+ public static Properties getProperties() {
+ if (PROPERTIES == null) {
+ synchronized (ConfigUtils.class) {
+ if (PROPERTIES == null) {
+ String path = System.getProperty(Constants.DUBBO_PROPERTIES_KEY);
+ if (path == null || path.length() == 0) {
+ path = System.getenv(Constants.DUBBO_PROPERTIES_KEY);
+ if (path == null || path.length() == 0) {
+ path = Constants.DEFAULT_DUBBO_PROPERTIES;
+ }
+ }
+ PROPERTIES = ConfigUtils.loadProperties(path, false, true);
+ }
+ }
+ }
+ return PROPERTIES;
+ }
+
+ public static void addProperties(Properties properties) {
+ if (properties != null) {
+ getProperties().putAll(properties);
+ }
+ }
+
+ public static void setProperties(Properties properties) {
+ if (properties != null) {
+ PROPERTIES = properties;
+ }
+ }
+
+ public static String getProperty(String key) {
+ return getProperty(key, null);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static String getProperty(String key, String defaultValue) {
+ String value = System.getProperty(key);
+ if (value != null && value.length() > 0) {
+ return value;
+ }
+ Properties properties = getProperties();
+ return replaceProperty(properties.getProperty(key, defaultValue), (Map)properties);
+ }
+
+ public static Properties loadProperties(String fileName) {
+ return loadProperties(fileName, false, false);
+ }
+
+ public static Properties loadProperties(String fileName, boolean allowMultiFile) {
+ return loadProperties(fileName, allowMultiFile, false);
+ }
+
+ /**
+ * Load properties file to {@link Properties} from class path.
+ *
+ * @param fileName properties file name. for example: <code>dubbo.properties</code>, <code>METE-INF/conf/foo.properties</code>
+ * @param allowMultiFile if <code>false</code>, throw {@link IllegalStateException} when found multi file on the class path.
+ * @return loaded {@link Properties} content. <ul>
+ * <li>return empty Properties if no file found.
+ * <li>merge multi properties file if found multi file
+ * </ul>
+ * @throws IllegalStateException not allow multi-file, but multi-file exsit on class path.
+ */
+ public static Properties loadProperties(String fileName, boolean allowMultiFile, boolean allowEmptyFile) {
+ Properties properties = new Properties();
+ if (fileName.startsWith("/")) {
+ try {
+ FileInputStream input = new FileInputStream(fileName);
+ try {
+ properties.load(input);
+ } finally {
+ input.close();
+ }
+ } catch (Throwable e) {
+ logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);
+ }
+ return properties;
+ }
+
+ List<java.net.URL> list = new ArrayList<java.net.URL>();
+ try {
+ Enumeration<java.net.URL> urls = ClassHelper.getClassLoader().getResources(fileName);
+ list = new ArrayList<java.net.URL>();
+ while (urls.hasMoreElements()) {
+ list.add(urls.nextElement());
+ }
+ } catch (Throwable t) {
+ logger.warn("Fail to load " + fileName + " file: " + t.getMessage(), t);
+ }
+
+ if(list.size() == 0) {
+ if (allowEmptyFile) {
+ logger.warn("No " + fileName + " found on the class path.");
+ }
+ return properties;
+ }
+
+ if(! allowMultiFile) {
+ if (list.size() > 1) {
+ String errMsg = String.format("only 1 %s file is expected, but %d dubbo.properties files found on class path: %s",
+ fileName, list.size(), list.toString());
+ logger.warn(errMsg);
+ // throw new IllegalStateException(errMsg); // see http://code.alibabatech.com/jira/browse/DUBBO-133
+ }
+ try {
+ properties.load(ClassHelper.getClassLoader().getResourceAsStream(fileName));
+ } catch (Throwable e) {
+ logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);
+ }
+ return properties;
+ }
+
+ logger.info("load " + fileName + " properties file from " + list);
+
+ for(java.net.URL url : list) {
+ try {
+ Properties p = new Properties();
+ InputStream input = url.openStream();
+ if (input != null) {
+ try {
+ p.load(input);
+ properties.putAll(p);
+ } finally {
+ try {
+ input.close();
+ } catch (Throwable t) {}
+ }
+ }
+ } catch (Throwable e) {
+ logger.warn("Fail to load " + fileName + " file from " + url + "(ingore this file): " + e.getMessage(), e);
+ }
+ }
+
+ return properties;
+ }
+
+ private static int PID = -1;
+
+ public static int getPid() {
+ if (PID < 0) {
+ try {
+ RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
+ String name = runtime.getName(); // format: "pid@hostname"
+ PID = Integer.parseInt(name.substring(0, name.indexOf('@')));
+ } catch (Throwable e) {
+ PID = 0;
+ }
+ }
+ return PID;
+ }
+
+ 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..ed2d5aa
--- /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..4659014
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/NetUtils.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.URL;
+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 (host == null || host.length() == 0) {
+ return host;
+ }
+ if (host.contains("://")) {
+ URL u = URL.valueOf(host);
+ if (NetUtils.isInvalidLocalHost(u.getHost())) {
+ return u.setHost(NetUtils.getLocalHost()).toFullString();
+ }
+ } else if (host.contains(":")) {
+ int i = host.lastIndexOf(':');
+ if (NetUtils.isInvalidLocalHost(host.substring(0, i))) {
+ return NetUtils.getLocalHost() + host.substring(i);
+ }
+ } else {
+ 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..034b35b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/PojoUtils.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * PojoUtils. Travel object deeply, and convert complex type to simple type.
+ * <p>
+ * Type below will be remained:
+ * <ul>
+ * <li> Primitive Type, also include <b>String</b>, <b>Number</b>(Integer, Long), <b>Date</b>
+ * <li> Array of Primitive Type
+ * <li> Collection, eg: List, Map, Set etc.
+ * </ul>
+ * <p>
+ * Other type will be covert to a map which contains the attributes and value pair of object.
+ *
+ * @author william.liangf
+ * @author ding.lid
+ */
+public class PojoUtils {
+
+ public static Object[] generalize(Object[] objs) {
+ Object[] dests = new Object[objs.length];
+ for (int i = 0; i < objs.length; i ++) {
+ dests[i] = generalize(objs[i]);
+ }
+ return dests;
+ }
+
+ public static Object[] realize(Object[] objs, Class<?>[] types) {
+ if (objs.length != types.length)
+ throw new IllegalArgumentException("args.length != types.length");
+ Object[] dests = new Object[objs.length];
+ for (int i = 0; i < objs.length; i ++) {
+ dests[i] = realize(objs[i], types[i]);
+ }
+ return dests;
+ }
+
+ public static Object[] realize(Object[] objs, Class<?>[] types, Type[] gtypes) {
+ if (objs.length != types.length
+ || objs.length != gtypes.length)
+ throw new IllegalArgumentException("args.length != types.length");
+ Object[] dests = new Object[objs.length];
+ for (int i = 0; i < objs.length; i ++) {
+ dests[i] = realize(objs[i], types[i], gtypes[i]);
+ }
+ return dests;
+ }
+
+ public static Object generalize(Object pojo) {
+ return generalize(pojo, new IdentityHashMap<Object, Object>());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object generalize(Object pojo, Map<Object, Object> history) {
+ if (pojo == null) {
+ return null;
+ }
+
+ if (pojo instanceof Enum<?>) {
+ return ((Enum<?>)pojo).name();
+ }
+ if (pojo.getClass().isArray()
+ && Enum.class.isAssignableFrom(
+ pojo.getClass().getComponentType())) {
+ int len = Array.getLength(pojo);
+ String[] values = new String[len];
+ for (int i = 0; i < len; i ++) {
+ values[i] = ((Enum<?>)Array.get(pojo, i)).name();
+ }
+ return values;
+ }
+
+ if (ReflectUtils.isPrimitives(pojo.getClass())) {
+ return pojo;
+ }
+
+ if (pojo instanceof Class) {
+ return ((Class)pojo).getName();
+ }
+
+ if (history.containsKey(pojo)) {
+ return history.get(pojo);
+ }
+ history.put(pojo, 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(pojo, map);
+ map.put("class", pojo.getClass().getName());
+ for (Method method : pojo.getClass().getMethods()) {
+ if (ReflectUtils.isBeanPropertyReadMethod(method)) {
+ try {
+ map.put(ReflectUtils.getPropertyNameFromBeanReadMethod(method),
+ generalize(method.invoke(pojo), history));
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ }
+ return map;
+ }
+
+ public static Object realize(Object pojo, Class<?> type) {
+ return realize0(pojo, type, null , new IdentityHashMap<Object, Object>());
+ }
+
+ public static Object realize(Object pojo, Class<?> type, Type genericType) {
+ return realize0(pojo, type, genericType, new IdentityHashMap<Object, 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 = realize0((Map<String, Object>) value, method.getReturnType(), null, new IdentityHashMap<Object, 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>();
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static Object realize0(Object pojo, Class<?> type, Type genericType, final Map<Object, Object> history) {
+ if (pojo == null) {
+ return null;
+ }
+
+ if (type != null && type.isEnum()
+ && pojo.getClass() == String.class) {
+ return Enum.valueOf((Class<Enum>)type, (String)pojo);
+ }
+
+ if (ReflectUtils.isPrimitives(pojo.getClass())
+ && ! (type != null && type.isArray()
+ && type.getComponentType().isEnum()
+ && pojo.getClass() == String[].class)) {
+ return CompatibleTypeUtils.compatibleTypeConvert(pojo, type);
+ }
+
+ if (history.containsKey(pojo)) {
+ return history.get(pojo);
+ }
+ history.put(pojo, 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 = realize0(obj, ctype, null, 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 = realize0(obj, ctype, null, 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 = realize0(obj, ctype, null, 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 = realize0(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() : realize0(entry.getKey(), keyClazz, keyType, history);
+ Object value = valueClazz == null ? entry.getValue() : realize0(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(pojo, dest);
+ return dest;
+ } else {
+ Object dest = newInstance(type);
+ history.put(pojo, 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 = realize0(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 + "(" + value.getClass() + "), 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 (ReflectUtils.isBeanPropertyWriteMethod(method)
+ && method.getName().equals(name)) {
+ return method;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static boolean isPojo(Class<?> cls) {
+ return ! ReflectUtils.isPrimitives(cls)
+ && ! Collection.class.isAssignableFrom(cls)
+ && ! Map.class.isAssignableFrom(cls);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Pool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Pool.java
new file mode 100644
index 0000000..01dd5a8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Pool.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+/**
+ * Pool.
+ *
+ * @author qian.lei
+ */
+
+public interface Pool<T>
+{
+ /**
+ * clear.
+ */
+ void clear() throws Exception;
+
+ /**
+ * close pool.
+ *
+ * @throws Exception.
+ */
+ void close() throws Exception;
+
+ /**
+ * borrow.
+ *
+ * @return object.
+ */
+ T borrowObject() throws Exception;
+
+ /**
+ * borrow.
+ *
+ * @param timeout timeout.
+ * @return object.
+ */
+ T borrowObject(long timeout) throws Exception;
+
+ /**
+ * return object.
+ *
+ * @param obj object.
+ */
+ void returnObject(T obj) throws Exception;
+
+ /**
+ * get factory.
+ *
+ * @return Factory instance.
+ */
+ Factory<T> getFactory();
+
+ /**
+ * get idle number.
+ *
+ * @return idle number.
+ */
+ int getIdleNum();
+
+ /**
+ * get active number.
+ *
+ * @return active number.
+ */
+ int getActiveNum();
+
+ /**
+ * pool factory.
+ */
+ public interface Factory<T>
+ {
+ /**
+ * make object.
+ *
+ * @return object.
+ * @throws Exception.
+ */
+ T makeObject() throws Exception;
+
+ /**
+ * destroy object.
+ *
+ * @param obj object.
+ * @throws Exception.
+ */
+ void destroyObject(T obj) throws Exception;
+
+ /**
+ * activate object.
+ *
+ * @param obj object.
+ */
+ void activateObject(T obj);
+
+ /**
+ * passivate object.
+ *
+ * @param obj object.
+ */
+ void passivateObject(T obj);
+
+// /**
+// * validate object.
+// *
+// * @param obj object.
+// * @return valid or not.
+// */
+// boolean validateObject(T obj);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Reference.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Reference.java
new file mode 100644
index 0000000..6e4c7fd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Reference.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+/**
+ * TODO Comment of Reference
+ *
+ * @author william.liangf
+ */
+public class Reference<T> {
+
+ private T value;
+
+ public void set(T value) {
+ this.value = value;
+ }
+
+ public T get() {
+ return value;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java
new file mode 100644
index 0000000..30079e3
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java
@@ -0,0 +1,963 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.GenericArrayType;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+
+/**
+ * ReflectUtils
+ *
+ * @author qian.lei
+ */
+public final class ReflectUtils {
+
+ /**
+ * void(V).
+ */
+ public static final char JVM_VOID = 'V';
+
+ /**
+ * boolean(Z).
+ */
+ public static final char JVM_BOOLEAN = 'Z';
+
+ /**
+ * byte(B).
+ */
+ public static final char JVM_BYTE = 'B';
+
+ /**
+ * char(C).
+ */
+ public static final char JVM_CHAR = 'C';
+
+ /**
+ * double(D).
+ */
+ public static final char JVM_DOUBLE = 'D';
+
+ /**
+ * float(F).
+ */
+ public static final char JVM_FLOAT = 'F';
+
+ /**
+ * int(I).
+ */
+ public static final char JVM_INT = 'I';
+
+ /**
+ * long(J).
+ */
+ public static final char JVM_LONG = 'J';
+
+ /**
+ * short(S).
+ */
+ public static final char JVM_SHORT = 'S';
+
+ public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
+
+ public static final String JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)";
+
+ public static final String JAVA_NAME_REGEX = "(?:" + JAVA_IDENT_REGEX + "(?:\\." + JAVA_IDENT_REGEX + ")*)";
+
+ public static final String CLASS_DESC = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)";
+
+ public static final String ARRAY_DESC = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))";
+
+ public static final String DESC_REGEX = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")";
+
+ public static final Pattern DESC_PATTERN = Pattern.compile(DESC_REGEX);
+
+ public static final String METHOD_DESC_REGEX = "(?:("+JAVA_IDENT_REGEX+")?\\(("+DESC_REGEX+"*)\\)("+DESC_REGEX+")?)";
+
+ public static final Pattern METHOD_DESC_PATTERN = Pattern.compile(METHOD_DESC_REGEX);
+
+ public static final Pattern GETTER_METHOD_DESC_PATTERN = Pattern.compile("get([A-Z][_a-zA-Z0-9]*)\\(\\)(" + DESC_REGEX + ")");
+
+ public static final Pattern SETTER_METHOD_DESC_PATTERN = Pattern.compile("set([A-Z][_a-zA-Z0-9]*)\\((" + DESC_REGEX + ")\\)V");
+
+ public static final Pattern IS_HAS_CAN_METHOD_DESC_PATTERN = Pattern.compile("(?:is|has|can)([A-Z][_a-zA-Z0-9]*)\\(\\)Z");
+
+ private static final ConcurrentMap<String, Class<?>> DESC_CLASS_CACHE = new ConcurrentHashMap<String, Class<?>>();
+
+ public static boolean isPrimitives(Class<?> cls) {
+ if (cls.isArray()) {
+ return isPrimitive(cls.getComponentType());
+ }
+ return isPrimitive(cls);
+ }
+
+ public static boolean isPrimitive(Class<?> cls) {
+ return cls.isPrimitive() || cls == String.class || cls == Boolean.class || cls == Character.class
+ || Number.class.isAssignableFrom(cls) || Date.class.isAssignableFrom(cls);
+ }
+
+ public static Class<?> getBoxedClass(Class<?> c) {
+ 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;
+ return c;
+ }
+
+ /**
+ * is compatible.
+ *
+ * @param c class.
+ * @param o instance.
+ * @return compatible or not.
+ */
+ public static boolean isCompatible(Class<?> c, Object o)
+ {
+ boolean pt = c.isPrimitive();
+ if( o == null )
+ return !pt;
+
+ if( pt )
+ {
+ if( c == int.class )
+ c = Integer.class;
+ else if( c == boolean.class )
+ c = Boolean.class;
+ else if( c == long.class )
+ c = Long.class;
+ else if( c == float.class )
+ c = Float.class;
+ else if( c == double.class )
+ c = Double.class;
+ else if( c == char.class )
+ c = Character.class;
+ else if( c == byte.class )
+ c = Byte.class;
+ else if( c == short.class )
+ c = Short.class;
+ }
+ if( c == o.getClass() )
+ return true;
+ return c.isInstance(o);
+ }
+
+ /**
+ * is compatible.
+ *
+ * @param cs class array.
+ * @param os object array.
+ * @return compatible or not.
+ */
+ public static boolean isCompatible(Class<?>[] cs, Object[] os)
+ {
+ int len = cs.length;
+ if( len != os.length ) return false;
+ if( len == 0 ) return true;
+ for(int i=0;i<len;i++)
+ if( !isCompatible(cs[i], os[i]) ) return false;
+ return true;
+ }
+
+ public static String getCodeBase(Class<?> cls) {
+ if (cls == null)
+ return null;
+ ProtectionDomain domain = cls.getProtectionDomain();
+ if (domain == null)
+ return null;
+ CodeSource source = domain.getCodeSource();
+ if (source == null)
+ return null;
+ URL location = source.getLocation();
+ if (location == null)
+ return null;
+ return location.getFile();
+ }
+
+ /**
+ * get name.
+ * java.lang.Object[][].class => "java.lang.Object[][]"
+ *
+ * @param c class.
+ * @return name.
+ */
+ public static String getName(Class<?> c)
+ {
+ if( c.isArray() )
+ {
+ StringBuilder sb = new StringBuilder();
+ do
+ {
+ sb.append("[]");
+ c = c.getComponentType();
+ }
+ while( c.isArray() );
+
+ return c.getName() + sb.toString();
+ }
+ return c.getName();
+ }
+
+
+ public static Class<?> getGenericClass(Class<?> cls) {
+ return getGenericClass(cls, 0);
+ }
+
+ public static Class<?> getGenericClass(Class<?> cls, int i) {
+ try {
+ ParameterizedType parameterizedType = ((ParameterizedType) cls.getGenericInterfaces()[0]);
+ Object genericClass = parameterizedType.getActualTypeArguments()[i];
+ if (genericClass instanceof ParameterizedType) { // 处理多级泛型
+ return (Class<?>) ((ParameterizedType) genericClass).getRawType();
+ } else if (genericClass instanceof GenericArrayType) { // 处理数组泛型
+ return (Class<?>) ((GenericArrayType) genericClass).getGenericComponentType();
+ } else {
+ return (Class<?>) genericClass;
+ }
+ } catch (Throwable e) {
+ throw new IllegalArgumentException(cls.getName()
+ + " generic type undefined!", e);
+ }
+ }
+
+
+ /**
+ * get method name.
+ * "void do(int)", "void do()", "int do(java.lang.String,boolean)"
+ *
+ * @param m method.
+ * @return name.
+ */
+ public static String getName(final Method m)
+ {
+ StringBuilder ret = new StringBuilder();
+ ret.append(getName(m.getReturnType())).append(' ');
+ ret.append(m.getName()).append('(');
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ {
+ if( i > 0 )
+ ret.append(',');
+ ret.append(getName(parameterTypes[i]));
+ }
+ ret.append(')');
+ return ret.toString();
+ }
+
+ public static String getSignature(String methodName, Class<?>[] parameterTypes) {
+ StringBuilder sb = new StringBuilder(methodName);
+ sb.append("(");
+ if (parameterTypes != null && parameterTypes.length > 0) {
+ boolean first = true;
+ for (Class<?> type : parameterTypes) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(type.getName());
+ }
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ /**
+ * get constructor name.
+ * "()", "(java.lang.String,int)"
+ *
+ * @param c constructor.
+ * @return name.
+ */
+ public static String getName(final Constructor<?> c)
+ {
+ StringBuilder ret = new StringBuilder("(");
+ Class<?>[] parameterTypes = c.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ {
+ if( i > 0 )
+ ret.append(',');
+ ret.append(getName(parameterTypes[i]));
+ }
+ ret.append(')');
+ return ret.toString();
+ }
+
+ /**
+ * get class desc.
+ * boolean[].class => "[Z"
+ * Object.class => "Ljava/lang/Object;"
+ *
+ * @param c class.
+ * @return desc.
+ * @throws NotFoundException
+ */
+ public static String getDesc(Class<?> c)
+ {
+ StringBuilder ret = new StringBuilder();
+
+ while( c.isArray() )
+ {
+ ret.append('[');
+ c = c.getComponentType();
+ }
+
+ if( c.isPrimitive() )
+ {
+ String t = c.getName();
+ if( "void".equals(t) ) ret.append(JVM_VOID);
+ else if( "boolean".equals(t) ) ret.append(JVM_BOOLEAN);
+ else if( "byte".equals(t) ) ret.append(JVM_BYTE);
+ else if( "char".equals(t) ) ret.append(JVM_CHAR);
+ else if( "double".equals(t) ) ret.append(JVM_DOUBLE);
+ else if( "float".equals(t) ) ret.append(JVM_FLOAT);
+ else if( "int".equals(t) ) ret.append(JVM_INT);
+ else if( "long".equals(t) ) ret.append(JVM_LONG);
+ else if( "short".equals(t) ) ret.append(JVM_SHORT);
+ }
+ else
+ {
+ ret.append('L');
+ ret.append(c.getName().replace('.', '/'));
+ ret.append(';');
+ }
+ return ret.toString();
+ }
+
+ /**
+ * get class array desc.
+ * [int.class, boolean[].class, Object.class] => "I[ZLjava/lang/Object;"
+ *
+ * @param cs class array.
+ * @return desc.
+ * @throws NotFoundException
+ */
+ public static String getDesc(final Class<?>[] cs)
+ {
+ if( cs.length == 0 )
+ return "";
+
+ StringBuilder sb = new StringBuilder(64);
+ for( Class<?> c : cs )
+ sb.append(getDesc(c));
+ return sb.toString();
+ }
+
+ /**
+ * get method desc.
+ * int do(int arg1) => "do(I)I"
+ * void do(String arg1,boolean arg2) => "do(Ljava/lang/String;Z)V"
+ *
+ * @param m method.
+ * @return desc.
+ */
+ public static String getDesc(final Method m)
+ {
+ StringBuilder ret = new StringBuilder(m.getName()).append('(');
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append(getDesc(m.getReturnType()));
+ return ret.toString();
+ }
+
+ /**
+ * get constructor desc.
+ * "()V", "(Ljava/lang/String;I)V"
+ *
+ * @param c constructor.
+ * @return desc
+ */
+ public static String getDesc(final Constructor<?> c)
+ {
+ StringBuilder ret = new StringBuilder("(");
+ Class<?>[] parameterTypes = c.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append('V');
+ return ret.toString();
+ }
+
+ /**
+ * get method desc.
+ * "(I)I", "()V", "(Ljava/lang/String;Z)V"
+ *
+ * @param m method.
+ * @return desc.
+ */
+ public static String getDescWithoutMethodName(Method m)
+ {
+ StringBuilder ret = new StringBuilder();
+ ret.append('(');
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append(getDesc(m.getReturnType()));
+ return ret.toString();
+ }
+
+ /**
+ * get class desc.
+ * Object.class => "Ljava/lang/Object;"
+ * boolean[].class => "[Z"
+ *
+ * @param c class.
+ * @return desc.
+ * @throws NotFoundException
+ */
+ public static String getDesc(final CtClass c) throws NotFoundException
+ {
+ StringBuilder ret = new StringBuilder();
+ if( c.isArray() )
+ {
+ ret.append('[');
+ ret.append(getDesc(c.getComponentType()));
+ }
+ else if( c.isPrimitive() )
+ {
+ String t = c.getName();
+ if( "void".equals(t) ) ret.append(JVM_VOID);
+ else if( "boolean".equals(t) ) ret.append(JVM_BOOLEAN);
+ else if( "byte".equals(t) ) ret.append(JVM_BYTE);
+ else if( "char".equals(t) ) ret.append(JVM_CHAR);
+ else if( "double".equals(t) ) ret.append(JVM_DOUBLE);
+ else if( "float".equals(t) ) ret.append(JVM_FLOAT);
+ else if( "int".equals(t) ) ret.append(JVM_INT);
+ else if( "long".equals(t) ) ret.append(JVM_LONG);
+ else if( "short".equals(t) ) ret.append(JVM_SHORT);
+ }
+ else
+ {
+ ret.append('L');
+ ret.append(c.getName().replace('.','/'));
+ ret.append(';');
+ }
+ return ret.toString();
+ }
+
+ /**
+ * get method desc.
+ * "do(I)I", "do()V", "do(Ljava/lang/String;Z)V"
+ *
+ * @param m method.
+ * @return desc.
+ */
+ public static String getDesc(final CtMethod m) throws NotFoundException
+ {
+ StringBuilder ret = new StringBuilder(m.getName()).append('(');
+ CtClass[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append(getDesc(m.getReturnType()));
+ return ret.toString();
+ }
+
+ /**
+ * get constructor desc.
+ * "()V", "(Ljava/lang/String;I)V"
+ *
+ * @param c constructor.
+ * @return desc
+ */
+ public static String getDesc(final CtConstructor c) throws NotFoundException
+ {
+ StringBuilder ret = new StringBuilder("(");
+ CtClass[] parameterTypes = c.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append('V');
+ return ret.toString();
+ }
+
+ /**
+ * get method desc.
+ * "(I)I", "()V", "(Ljava/lang/String;Z)V".
+ *
+ * @param m method.
+ * @return desc.
+ */
+ public static String getDescWithoutMethodName(final CtMethod m) throws NotFoundException
+ {
+ StringBuilder ret = new StringBuilder();
+ ret.append('(');
+ CtClass[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append(getDesc(m.getReturnType()));
+ return ret.toString();
+ }
+
+ /**
+ * name to desc.
+ * java.util.Map[][] => "[[Ljava/util/Map;"
+ *
+ * @param name name.
+ * @return desc.
+ */
+ public static String name2desc(String name)
+ {
+ StringBuilder sb = new StringBuilder();
+ int c = 0,index = name.indexOf('[');
+ if( index > 0 )
+ {
+ c = ( name.length() - index ) / 2;
+ name = name.substring(0,index);
+ }
+ while( c-- > 0 ) sb.append("[");
+ if( "void".equals(name) ) sb.append(JVM_VOID);
+ else if( "boolean".equals(name) ) sb.append(JVM_BOOLEAN);
+ else if( "byte".equals(name) ) sb.append(JVM_BYTE);
+ else if( "char".equals(name) ) sb.append(JVM_CHAR);
+ else if( "double".equals(name) ) sb.append(JVM_DOUBLE);
+ else if( "float".equals(name) ) sb.append(JVM_FLOAT);
+ else if( "int".equals(name) ) sb.append(JVM_INT);
+ else if( "long".equals(name) ) sb.append(JVM_LONG);
+ else if( "short".equals(name) ) sb.append(JVM_SHORT);
+ else sb.append('L').append(name.replace('.', '/')).append(';');
+ return sb.toString();
+ }
+
+ /**
+ * desc to name.
+ * "[[I" => "int[][]"
+ *
+ * @param desc desc.
+ * @return name.
+ */
+ public static String desc2name(String desc)
+ {
+ StringBuilder sb = new StringBuilder();
+ int c = desc.lastIndexOf('[') + 1;
+ if( desc.length() == c+1 )
+ {
+ switch( desc.charAt(c) )
+ {
+ case JVM_VOID: { sb.append("void"); break; }
+ case JVM_BOOLEAN: { sb.append("boolean"); break; }
+ case JVM_BYTE: { sb.append("byte"); break; }
+ case JVM_CHAR: { sb.append("char"); break; }
+ case JVM_DOUBLE: { sb.append("double"); break; }
+ case JVM_FLOAT: { sb.append("float"); break; }
+ case JVM_INT: { sb.append("int"); break; }
+ case JVM_LONG: { sb.append("long"); break; }
+ case JVM_SHORT: { sb.append("short"); break; }
+ default:
+ throw new RuntimeException();
+ }
+ }
+ else
+ {
+ sb.append(desc.substring(c+1, desc.length()-1).replace('/','.'));
+ }
+ while( c-- > 0 ) sb.append("[]");
+ return sb.toString();
+ }
+
+ public static Class<?> forName(String name) {
+ try {
+ return name2class(name);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("Not found class " + name + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * name to class.
+ * "boolean" => boolean.class
+ * "java.util.Map[][]" => java.util.Map[][].class
+ *
+ * @param name name.
+ * @return Class instance.
+ */
+ public static Class<?> name2class(String name) throws ClassNotFoundException
+ {
+ return name2class(ClassHelper.getClassLoader(), name);
+ }
+
+ /**
+ * name to class.
+ * "boolean" => boolean.class
+ * "java.util.Map[][]" => java.util.Map[][].class
+ *
+ * @param cl ClassLoader instance.
+ * @param name name.
+ * @return Class instance.
+ */
+ private static Class<?> name2class(ClassLoader cl, String name) throws ClassNotFoundException
+ {
+ int c = 0, index = name.indexOf('[');
+ if( index > 0 )
+ {
+ c = ( name.length() - index ) / 2;
+ name = name.substring(0, index);
+ }
+ if( c > 0 )
+ {
+ StringBuilder sb = new StringBuilder();
+ while( c-- > 0 )
+ sb.append("[");
+
+ if( "void".equals(name) ) sb.append(JVM_VOID);
+ else if( "boolean".equals(name) ) sb.append(JVM_BOOLEAN);
+ else if( "byte".equals(name) ) sb.append(JVM_BYTE);
+ else if( "char".equals(name) ) sb.append(JVM_CHAR);
+ else if( "double".equals(name) ) sb.append(JVM_DOUBLE);
+ else if( "float".equals(name) ) sb.append(JVM_FLOAT);
+ else if( "int".equals(name) ) sb.append(JVM_INT);
+ else if( "long".equals(name) ) sb.append(JVM_LONG);
+ else if( "short".equals(name) ) sb.append(JVM_SHORT);
+ else sb.append('L').append(name).append(';'); // "java.lang.Object" ==> "Ljava.lang.Object;"
+ name = sb.toString();
+ }
+ else
+ {
+ if( "void".equals(name) ) return void.class;
+ else if( "boolean".equals(name) ) return boolean.class;
+ else if( "byte".equals(name) ) return byte.class;
+ else if( "char".equals(name) ) return char.class;
+ else if( "double".equals(name) ) return double.class;
+ else if( "float".equals(name) ) return float.class;
+ else if( "int".equals(name) ) return int.class;
+ else if( "long".equals(name) ) return long.class;
+ else if( "short".equals(name) ) return short.class;
+ }
+
+ if( cl == null )
+ cl = ClassHelper.getClassLoader();
+ return Class.forName(name, true, cl);
+ }
+
+ /**
+ * desc to class.
+ * "[Z" => boolean[].class
+ * "[[Ljava/util/Map;" => java.util.Map[][].class
+ *
+ * @param desc desc.
+ * @return Class instance.
+ * @throws ClassNotFoundException
+ */
+ public static Class<?> desc2class(String desc) throws ClassNotFoundException
+ {
+ return desc2class(ClassHelper.getClassLoader(), desc);
+ }
+
+ /**
+ * desc to class.
+ * "[Z" => boolean[].class
+ * "[[Ljava/util/Map;" => java.util.Map[][].class
+ *
+ * @param cl ClassLoader instance.
+ * @param desc desc.
+ * @return Class instance.
+ * @throws ClassNotFoundException
+ */
+ private static Class<?> desc2class(ClassLoader cl, String desc) throws ClassNotFoundException
+ {
+ switch( desc.charAt(0) )
+ {
+ case JVM_VOID: return void.class;
+ case JVM_BOOLEAN: return boolean.class;
+ case JVM_BYTE: return byte.class;
+ case JVM_CHAR: return char.class;
+ case JVM_DOUBLE: return double.class;
+ case JVM_FLOAT: return float.class;
+ case JVM_INT: return int.class;
+ case JVM_LONG: return long.class;
+ case JVM_SHORT: return short.class;
+ case 'L':
+ desc = desc.substring(1, desc.length()-1).replace('/', '.'); // "Ljava/lang/Object;" ==> "java.lang.Object"
+ break;
+ case '[':
+ desc = desc.replace('/', '.'); // "[[Ljava/lang/Object;" ==> "[[Ljava.lang.Object;"
+ break;
+ default:
+ throw new ClassNotFoundException("Class not found: " + desc);
+ }
+
+ if( cl == null )
+ cl = ClassHelper.getClassLoader();
+ Class<?> clazz = DESC_CLASS_CACHE.get(desc);
+ if(clazz==null){
+ clazz = Class.forName(desc, true, cl);
+ DESC_CLASS_CACHE.put(desc, clazz);
+ }
+ return clazz;
+ }
+
+ /**
+ * get class array instance.
+ *
+ * @param desc desc.
+ * @return Class class array.
+ * @throws ClassNotFoundException
+ */
+ public static Class<?>[] desc2classArray(String desc) throws ClassNotFoundException
+ {
+ Class<?>[] ret = desc2classArray(ClassHelper.getClassLoader(), desc);
+ return ret;
+ }
+
+ /**
+ * get class array instance.
+ *
+ * @param cl ClassLoader instance.
+ * @param desc desc.
+ * @return Class[] class array.
+ * @throws ClassNotFoundException
+ */
+ private static Class<?>[] desc2classArray(ClassLoader cl, String desc) throws ClassNotFoundException
+ {
+ if( desc.length() == 0 )
+ return EMPTY_CLASS_ARRAY;
+
+ List<Class<?>> cs = new ArrayList<Class<?>>();
+ Matcher m = DESC_PATTERN.matcher(desc);
+ while(m.find())
+ cs.add(desc2class(cl, m.group()));
+ return cs.toArray(EMPTY_CLASS_ARRAY);
+ }
+
+ /**
+ * 根据方法签名从类中找出方法。
+ *
+ * @param clazz 查找的类。
+ * @param methodName 方法签名,形如method1(int, String)。也允许只给方法名不参数只有方法名,形如method2。
+ * @return 返回查找到的方法。
+ * @throws NoSuchMethodException
+ * @throws ClassNotFoundException
+ * @throws IllegalStateException 给定的方法签名找到多个方法(方法签名中没有指定参数,又有有重载的方法的情况)
+ */
+ public static Method findMethodByMethodSignature(Class<?> clazz, String methodName, String[] parameterTypes)
+ throws NoSuchMethodException, ClassNotFoundException {
+ if (parameterTypes == null) {
+ List<Method> finded = new ArrayList<Method>();
+ for (Method m : clazz.getMethods()) {
+ if (m.getName().equals(methodName)) {
+ finded.add(m);
+ }
+ }
+ if (finded.isEmpty()) {
+ throw new NoSuchMethodException("No such method " + methodName + " in class " + clazz);
+ }
+ if(finded.size() > 1) {
+ String msg = String.format("Not unique method for method name(%s) in class(%s), find %d methods.",
+ methodName, clazz.getName(), finded.size());
+ throw new IllegalStateException(msg);
+ }
+ return finded.get(0);
+ } else {
+ Class<?>[] types = new Class<?>[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i ++) {
+ types[i] = ReflectUtils.name2class(parameterTypes[i]);
+ }
+ return clazz.getMethod(methodName, types);
+ }
+ }
+
+ public static Method findMethodByMethodName(Class<?> clazz, String methodName)
+ throws NoSuchMethodException, ClassNotFoundException {
+ return findMethodByMethodSignature(clazz, methodName, null);
+ }
+
+ public static Constructor<?> findConstructor(Class<?> clazz, Class<?> paramType) throws NoSuchMethodException {
+ Constructor<?> targetConstructor;
+ try {
+ targetConstructor = clazz.getConstructor(new Class<?>[] {paramType});
+ } catch (NoSuchMethodException e) {
+ targetConstructor = null;
+ Constructor<?>[] constructors = clazz.getConstructors();
+ for (Constructor<?> constructor : constructors) {
+ if (Modifier.isPublic(constructor.getModifiers())
+ && constructor.getParameterTypes().length == 1
+ && constructor.getParameterTypes()[0].isAssignableFrom(paramType)) {
+ targetConstructor = constructor;
+ break;
+ }
+ }
+ if (targetConstructor == null) {
+ throw e;
+ }
+ }
+ return targetConstructor;
+ }
+
+ /**
+ * 检查对象是否是指定接口的实现。
+ * <p>
+ * 不会触发到指定接口的{@link Class},所以如果ClassLoader中没有指定接口类时,也不会出错。
+ *
+ * @param obj 要检查的对象
+ * @param interfaceClazzName 指定的接口名
+ * @return 返回{@code true},如果对象实现了指定接口;否则返回{@code false}。
+ */
+ public static boolean isInstance(Object obj, String interfaceClazzName) {
+ for (Class<?> clazz = obj.getClass();
+ clazz != null && !clazz.equals(Object.class);
+ clazz = clazz.getSuperclass()) {
+ Class<?>[] interfaces = clazz.getInterfaces();
+ for (Class<?> itf : interfaces) {
+ if (itf.getName().equals(interfaceClazzName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static Object getEmptyObject(Class<?> returnType) {
+ return getEmptyObject(returnType, new HashMap<Class<?>, Object>(), 0);
+ }
+
+ private static Object getEmptyObject(Class<?> returnType, Map<Class<?>, Object> emptyInstances, int level) {
+ if (level > 2)
+ return null;
+ if (returnType == null) {
+ return null;
+ } else if (returnType == boolean.class || returnType == Boolean.class) {
+ return false;
+ } else if (returnType == char.class || returnType == Character.class) {
+ return '\0';
+ } else if (returnType == byte.class || returnType == Byte.class) {
+ return (byte)0;
+ } else if (returnType == short.class || returnType == Short.class) {
+ return (short)0;
+ } else if (returnType == int.class || returnType == Integer.class) {
+ return 0;
+ } else if (returnType == long.class || returnType == Long.class) {
+ return 0L;
+ } else if (returnType == float.class || returnType == Float.class) {
+ return 0F;
+ } else if (returnType == double.class || returnType == Double.class) {
+ return 0D;
+ } else if (returnType.isArray()) {
+ return Array.newInstance(returnType.getComponentType(), 0);
+ } else if (returnType.isAssignableFrom(ArrayList.class)) {
+ return new ArrayList<Object>(0);
+ } else if (returnType.isAssignableFrom(HashSet.class)) {
+ return new HashSet<Object>(0);
+ } else if (returnType.isAssignableFrom(HashMap.class)) {
+ return new HashMap<Object, Object>(0);
+ } else if (String.class.equals(returnType)) {
+ return "";
+ } else if (! returnType.isInterface()) {
+ try {
+ Object value = emptyInstances.get(returnType);
+ if (value == null) {
+ value = returnType.newInstance();
+ emptyInstances.put(returnType, value);
+ }
+ Class<?> cls = value.getClass();
+ while (cls != null && cls != Object.class) {
+ Field[] fields = cls.getDeclaredFields();
+ for (Field field : fields) {
+ Object property = getEmptyObject(field.getType(), emptyInstances, level + 1);
+ if (property != null) {
+ try {
+ if (! field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ field.set(value, property);
+ } catch (Throwable e) {
+ }
+ }
+ }
+ cls = cls.getSuperclass();
+ }
+ return value;
+ } catch (Throwable e) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public static boolean isBeanPropertyReadMethod(Method method) {
+ return method != null
+ && Modifier.isPublic(method.getModifiers())
+ && ! Modifier.isStatic(method.getModifiers())
+ && method.getReturnType() != void.class
+ && method.getDeclaringClass() != Object.class
+ && method.getParameterTypes().length == 0
+ && (method.getName().startsWith("get") || method.getName().startsWith("is"));
+ }
+
+ public static String getPropertyNameFromBeanReadMethod(Method method) {
+ if (isBeanPropertyReadMethod(method)) {
+ if (method.getName().startsWith("get")) {
+ return method.getName().substring(3, 4).toLowerCase()
+ + method.getName().substring(4);
+ }
+ if (method.getName().startsWith("is")) {
+ return method.getName().substring(2, 3).toLowerCase()
+ + method.getName().substring(3);
+ }
+ }
+ return null;
+ }
+
+ public static boolean isBeanPropertyWriteMethod(Method method) {
+ return method != null
+ && Modifier.isPublic(method.getModifiers())
+ && ! Modifier.isStatic(method.getModifiers())
+ && method.getDeclaringClass() != Object.class
+ && method.getParameterTypes().length == 1
+ && method.getName().startsWith("set");
+ }
+
+ public static String getPropertyNameFromBeanWriteMethod(Method method) {
+ if (isBeanPropertyWriteMethod(method)) {
+ return method.getName().substring(3, 4).toLowerCase()
+ + method.getName().substring(4);
+ }
+ return null;
+ }
+
+ private ReflectUtils(){}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Stack.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Stack.java
new file mode 100644
index 0000000..ab4e121
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Stack.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.util.ArrayList;
+import java.util.EmptyStackException;
+import java.util.List;
+
+/**
+ * Stack.
+ *
+ * @author qian.lei
+ */
+
+public class Stack<E>
+{
+ private int mSize = 0;
+
+ private List<E> mElements = new ArrayList<E>();
+
+ public Stack(){}
+
+ /**
+ * push.
+ *
+ * @param ele
+ */
+ public void push(E ele)
+ {
+ if( mElements.size() > mSize )
+ mElements.set(mSize, ele);
+ else
+ mElements.add(ele);
+ mSize++;
+ }
+
+ /**
+ * pop.
+ *
+ * @return the last element.
+ */
+ public E pop()
+ {
+ if( mSize == 0 )
+ throw new EmptyStackException();
+ return mElements.set(--mSize, null);
+ }
+
+ /**
+ * peek.
+ *
+ * @return the last element.
+ */
+ public E peek()
+ {
+ if( mSize == 0 )
+ throw new EmptyStackException();
+ return mElements.get(mSize-1);
+ }
+
+ /**
+ * get.
+ *
+ * @param index index.
+ * @return element.
+ */
+ public E get(int index)
+ {
+ if( index >= mSize )
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mSize);
+
+ return index < 0 ? mElements.get(index+mSize) : mElements.get(index);
+ }
+
+ /**
+ * set.
+ *
+ * @param index index.
+ * @param value element.
+ * @return old element.
+ */
+ public E set(int index, E value)
+ {
+ if( index >= mSize )
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mSize);
+
+ return mElements.set(index < 0 ? index + mSize : index, value);
+ }
+
+ /**
+ * remove.
+ *
+ * @param index
+ * @return element
+ */
+ public E remove(int index)
+ {
+ if( index >= mSize )
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mSize);
+
+ E ret = mElements.remove(index < 0 ? index + mSize : index);
+ mSize--;
+ return ret;
+ }
+
+ /**
+ * get stack size.
+ *
+ * @return size.
+ */
+ public int size()
+ {
+ return mSize;
+ }
+
+ /**
+ * is empty.
+ *
+ * @return empty or not.
+ */
+ public boolean isEmpty()
+ {
+ return mSize == 0;
+ }
+
+ /**
+ * clear stack.
+ */
+ public void clear()
+ {
+ mSize = 0;
+ mElements.clear();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/StringUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/StringUtils.java
new file mode 100644
index 0000000..01186b9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/StringUtils.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.io.UnsafeStringWriter;
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * StringUtils
+ *
+ * @author qian.lei
+ */
+
+public final class StringUtils {
+
+ private static final Logger logger = LoggerFactory.getLogger(StringUtils.class);
+
+ public static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private static final Pattern KVP_PATTERN = Pattern.compile("([_.a-zA-Z0-9][-_.a-zA-Z0-9]*)[=](.*)"); //key value pair pattern.
+
+ private static final Pattern INT_PATTERN = Pattern.compile("^\\d+$");
+
+ public static boolean isBlank(String str)
+ {
+ if( str == null || str.length() == 0 )
+ return true;
+ return false;
+ }
+
+ /**
+ * is empty string.
+ *
+ * @param str source string.
+ * @return is empty.
+ */
+ public static boolean isEmpty(String str)
+ {
+ if( str == null || str.length() == 0 )
+ return true;
+ return false;
+ }
+
+ /**
+ * is not empty string.
+ *
+ * @param str source string.
+ * @return is not empty.
+ */
+ public static boolean isNotEmpty(String str)
+ {
+ return str != null && str.length() > 0;
+ }
+
+ /**
+ *
+ * @param s1
+ * @param s2
+ * @return equals
+ */
+ public static boolean isEquals(String s1, String s2) {
+ if (s1 == null && s2 == null)
+ return true;
+ if (s1 == null || s2 == null)
+ return false;
+ return s1.equals(s2);
+ }
+
+ /**
+ * is integer string.
+ *
+ * @param str
+ * @return is integer
+ */
+ public static boolean isInteger(String str) {
+ if (str == null || str.length() == 0)
+ return false;
+ return INT_PATTERN.matcher(str).matches();
+ }
+
+ public static int parseInteger(String str) {
+ if (! isInteger(str))
+ return 0;
+ return Integer.parseInt(str);
+ }
+
+ /**
+ * Returns true if s is a legal Java identifier.<p>
+ * <a href="http://www.exampledepot.com/egs/java.lang/IsJavaId.html">more info.</a>
+ */
+ public static boolean isJavaIdentifier(String s) {
+ if (s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) {
+ return false;
+ }
+ for (int i=1; i<s.length(); i++) {
+ if (!Character.isJavaIdentifierPart(s.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isContains(String values, String value) {
+ if (values == null || values.length() == 0) {
+ return false;
+ }
+ return isContains(Constants.COMMA_SPLIT_PATTERN.split(values), value);
+ }
+
+ /**
+ *
+ * @param values
+ * @param value
+ * @return contains
+ */
+ public static boolean isContains(String[] values, String value) {
+ if (value != null && value.length() > 0 && values != null && values.length > 0) {
+ for (String v : values) {
+ if (value.equals(v)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean isNumeric(String str) {
+ if (str == null) {
+ return false;
+ }
+ int sz = str.length();
+ for (int i = 0; i < sz; i++) {
+ if (Character.isDigit(str.charAt(i)) == false) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param e
+ * @return string
+ */
+ public static String toString(Throwable e) {
+ UnsafeStringWriter w = new UnsafeStringWriter();
+ PrintWriter p = new PrintWriter(w);
+ p.print(e.getClass().getName());
+ if (e.getMessage() != null) {
+ p.print(": " + e.getMessage());
+ }
+ p.println();
+ try {
+ e.printStackTrace(p);
+ return w.toString();
+ } finally {
+ p.close();
+ }
+ }
+
+ /**
+ *
+ * @param msg
+ * @param e
+ * @return string
+ */
+ public static String toString(String msg, Throwable e) {
+ UnsafeStringWriter w = new UnsafeStringWriter();
+ w.write(msg + "\n");
+ PrintWriter p = new PrintWriter(w);
+ try {
+ e.printStackTrace(p);
+ return w.toString();
+ } finally {
+ p.close();
+ }
+ }
+
+ /**
+ * translat.
+ *
+ * @param src source string.
+ * @param from src char table.
+ * @param to target char table.
+ * @return String.
+ */
+ public static String translat(String src, String from, String to)
+ {
+ if( isEmpty(src) ) return src;
+ StringBuilder sb = null;
+ int ix;
+ char c;
+ for(int i=0,len=src.length();i<len;i++)
+ {
+ c = src.charAt(i);
+ ix = from.indexOf(c);
+ if( ix == -1 )
+ {
+ if( sb != null )
+ sb.append(c);
+ }
+ else
+ {
+ if( sb == null )
+ {
+ sb = new StringBuilder(len);
+ sb.append(src, 0, i);
+ }
+ if( ix < to.length() )
+ sb.append(to.charAt(ix));
+ }
+ }
+ return sb == null ? src : sb.toString();
+ }
+
+ /**
+ * split.
+ *
+ * @param ch char.
+ * @return string array.
+ */
+ public static String[] split(String str, char ch)
+ {
+ List<String> list = null;
+ char c;
+ int ix = 0,len=str.length();
+ for(int i=0;i<len;i++)
+ {
+ c = str.charAt(i);
+ if( c == ch )
+ {
+ if( list == null )
+ list = new ArrayList<String>();
+ list.add(str.substring(ix, i));
+ ix = i + 1;
+ }
+ }
+ if( ix > 0 )
+ list.add(str.substring(ix));
+ return list == null ? EMPTY_STRING_ARRAY : (String[])list.toArray(EMPTY_STRING_ARRAY);
+ }
+
+ /**
+ * join string.
+ *
+ * @param array String array.
+ * @return String.
+ */
+ public static String join(String[] array)
+ {
+ if( array.length == 0 ) return "";
+ StringBuilder sb = new StringBuilder();
+ for( String s : array )
+ sb.append(s);
+ return sb.toString();
+ }
+
+ /**
+ * join string like javascript.
+ *
+ * @param array String array.
+ * @param split split
+ * @return String.
+ */
+ public static String join(String[] array, char split)
+ {
+ if( array.length == 0 ) return "";
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<array.length;i++)
+ {
+ if( i > 0 )
+ sb.append(split);
+ sb.append(array[i]);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * join string like javascript.
+ *
+ * @param array String array.
+ * @param split split
+ * @return String.
+ */
+ public static String join(String[] array, String split)
+ {
+ if( array.length == 0 ) return "";
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<array.length;i++)
+ {
+ if( i > 0 )
+ sb.append(split);
+ sb.append(array[i]);
+ }
+ return sb.toString();
+ }
+
+ public static String join(Collection<String> coll, String split) {
+ if(coll.isEmpty()) return "";
+
+ StringBuilder sb = new StringBuilder();
+ boolean isFirst = true;
+ for(String s : coll) {
+ if(isFirst) isFirst = false; else sb.append(split);
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * parse key-value pair.
+ *
+ * @param str string.
+ * @param itemSeparator item separator.
+ * @return key-value map;
+ */
+ private static Map<String, String> parseKeyValuePair(String str, String itemSeparator)
+ {
+ String[] tmp = str.split(itemSeparator);
+ Map<String, String> map = new HashMap<String, String>(tmp.length);
+ for(int i=0;i<tmp.length;i++)
+ {
+ Matcher matcher = KVP_PATTERN.matcher(tmp[i]);
+ if( matcher.matches() == false )
+ continue;
+ map.put(matcher.group(1), matcher.group(2));
+ }
+ return map;
+ }
+
+ public static String getQueryStringValue(String qs, String key) {
+ Map<String, String> map = StringUtils.parseQueryString(qs);
+ return map.get(key);
+ }
+
+ /**
+ * parse query string to Parameters.
+ *
+ * @param qs query string.
+ * @return Parameters instance.
+ */
+ public static Map<String, String> parseQueryString(String qs)
+ {
+ if( qs == null || qs.length() == 0 )
+ return new HashMap<String, String>();
+ return parseKeyValuePair(qs, "\\&");
+ }
+
+ public static String getServiceKey(Map<String, String> ps) {
+ StringBuilder buf = new StringBuilder();
+ String group = ps.get(Constants.GROUP_KEY);
+ if (group != null && group.length()>0){
+ buf.append(group).append("/");
+ }
+ buf.append(ps.get(Constants.INTERFACE_KEY));
+ String version = ps.get(Constants.VERSION_KEY);
+ if (version!= null && version.length()>0){
+ buf.append(":").append(version);
+ }
+ return buf.toString();
+ }
+
+ public static String toQueryString(Map<String, String> ps) {
+ StringBuilder buf = new StringBuilder();
+ if (ps != null && ps.size() > 0) {
+ for (Map.Entry<String, String> entry : new TreeMap<String, String>(ps).entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key != null && key.length() > 0
+ && value != null && value.length() > 0) {
+ if (buf.length() > 0) {
+ buf.append("&");
+ }
+ buf.append(key);
+ buf.append("=");
+ buf.append(value);
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ public static String camelToSplitName(String camelName, String split) {
+ if (camelName == null || camelName.length() == 0) {
+ return camelName;
+ }
+ StringBuilder buf = null;
+ for (int i = 0; i < camelName.length(); i ++) {
+ char ch = camelName.charAt(i);
+ if (ch >= 'A' && ch <= 'Z') {
+ if (buf == null) {
+ buf = new StringBuilder();
+ if (i > 0) {
+ buf.append(camelName.substring(0, i));
+ }
+ }
+ if (i > 0) {
+ buf.append(split);
+ }
+ buf.append(Character.toLowerCase(ch));
+ } else if (buf != null) {
+ buf.append(ch);
+ }
+ }
+ return buf == null ? camelName : buf.toString();
+ }
+
+ public static String toArgumentString(Object[] args) {
+ StringBuilder buf = new StringBuilder();
+ for (Object arg : args) {
+ if (buf.length() > 0) {
+ buf.append(Constants.COMMA_SEPARATOR);
+ }
+ if (arg == null || ReflectUtils.isPrimitives(arg.getClass())) {
+ buf.append(arg);
+ } else {
+ try {
+ buf.append(JSON.json(arg));
+ } catch (IOException e) {
+ logger.warn(e.getMessage(), e);
+ buf.append(arg);
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ private StringUtils(){}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java
new file mode 100644
index 0000000..fae8e66
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.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 (u.isAnyHost() || u.isLocalHost()) {
+ changed = true;
+ host = NetUtils.getLocalHost();
+ }
+ 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<URL> subscribed) {
+ if (forbid != null && forbid.size() > 0) {
+ List<String> newForbid = new ArrayList<String>();
+ for (String serviceName : forbid) {
+ if (! serviceName.contains(":") && ! serviceName.contains("/")) {
+ for (URL url : subscribed) {
+ if (serviceName.equals(url.getServiceInterface())) {
+ newForbid.add(url.getServiceKey());
+ break;
+ }
+ }
+ } else {
+ newForbid.add(serviceName);
+ }
+ }
+ return newForbid;
+ }
+ return forbid;
+ }
+
+ public static URL getEmptyUrl(String service, String category) {
+ String group = null;
+ String version = null;
+ int i = service.indexOf('/');
+ if (i > 0) {
+ group = service.substring(0, i);
+ service = service.substring(i + 1);
+ }
+ i = service.lastIndexOf(':');
+ if (i > 0) {
+ version = service.substring(i + 1);
+ service = service.substring(0, i);
+ }
+ return URL.valueOf(Constants.EMPTY_PROTOCOL + "://0.0.0.0/" + service + "?"
+ + Constants.CATEGORY_KEY + "=" + category
+ + (group == null ? "" : "&" + Constants.GROUP_KEY + "=" + group)
+ + (version == null ? "" : "&" + Constants.VERSION_KEY + "=" + version));
+ }
+
+ public static boolean isMatchCategory(String category, String categories) {
+ if (categories == null || categories.length() == 0) {
+ return Constants.DEFAULT_CATEGORY.equals(category);
+ } else if (categories.contains(Constants.ANY_VALUE)) {
+ return true;
+ } else if (categories.contains(Constants.REMOVE_VALUE_PREFIX)) {
+ return ! categories.contains(Constants.REMOVE_VALUE_PREFIX + category);
+ } else {
+ return categories.contains(category);
+ }
+ }
+
+ public static boolean isMatch(URL consumerUrl, URL providerUrl) {
+ String consumerInterface = consumerUrl.getServiceInterface();
+ String providerInterface = providerUrl.getServiceInterface();
+ if( ! (Constants.ANY_VALUE.equals(consumerInterface) || StringUtils.isEquals(consumerInterface, providerInterface)) ) return false;
+
+ if (! isMatchCategory(providerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY),
+ consumerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY))) {
+ return false;
+ }
+ if (! providerUrl.getParameter(Constants.ENABLED_KEY, true)
+ && ! Constants.ANY_VALUE.equals(consumerUrl.getParameter(Constants.ENABLED_KEY))) {
+ return false;
+ }
+
+ String consumerGroup = consumerUrl.getParameter(Constants.GROUP_KEY);
+ String consumerVersion = consumerUrl.getParameter(Constants.VERSION_KEY);
+ String consumerClassifier = consumerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
+
+ String providerGroup = providerUrl.getParameter(Constants.GROUP_KEY);
+ String providerVersion = providerUrl.getParameter(Constants.VERSION_KEY);
+ String providerClassifier = providerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
+ return (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
+ && (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
+ && (consumerClassifier == null || Constants.ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));
+ }
+
+ public static boolean isMatchGlobPattern(String pattern, String value, URL param) {
+ if (param != null && pattern.startsWith("$")) {
+ pattern = param.getRawParameter(pattern.substring(1));
+ }
+ return isMatchGlobPattern(pattern, value);
+ }
+
+ public static boolean isMatchGlobPattern(String pattern, String value) {
+ if ("*".equals(pattern))
+ return true;
+ if((pattern == null || pattern.length() == 0)
+ && (value == null || value.length() == 0))
+ return true;
+ if((pattern == null || pattern.length() == 0)
+ || (value == null || value.length() == 0))
+ return false;
+
+ int i = pattern.lastIndexOf('*');
+ // 没有找到星号
+ if(i == -1) {
+ return value.equals(pattern);
+ }
+ // 星号在末尾
+ else if (i == pattern.length() - 1) {
+ return value.startsWith(pattern.substring(0, i));
+ }
+ // 星号的开头
+ else if (i == 0) {
+ return value.endsWith(pattern.substring(i + 1));
+ }
+ // 星号的字符串的中间
+ else {
+ String prefix = pattern.substring(0, i);
+ String suffix = pattern.substring(i + 1);
+ return value.startsWith(prefix) && value.endsWith(suffix);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler
new file mode 100644
index 0000000..45d649e
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler
@@ -0,0 +1,3 @@
+adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
+jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler
+javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory
new file mode 100644
index 0000000..21bf88f
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory
@@ -0,0 +1,2 @@
+adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
+spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.logger.LoggerAdapter b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.logger.LoggerAdapter
new file mode 100644
index 0000000..7ad69e0
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.logger.LoggerAdapter
@@ -0,0 +1,4 @@
+slf4j=com.alibaba.dubbo.common.logger.slf4j.Slf4jLoggerAdapter
+jcl=com.alibaba.dubbo.common.logger.jcl.JclLoggerAdapter
+log4j=com.alibaba.dubbo.common.logger.log4j.Log4jLoggerAdapter
+jdk=com.alibaba.dubbo.common.logger.jdk.JdkLoggerAdapter
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization
new file mode 100644
index 0000000..fec7fc4
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization
@@ -0,0 +1,6 @@
+dubbo=com.alibaba.dubbo.common.serialize.support.dubbo.DubboSerialization
+hessian2=com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization
+java=com.alibaba.dubbo.common.serialize.support.java.JavaSerialization
+compactedjava=com.alibaba.dubbo.common.serialize.support.java.CompactedJavaSerialization
+json=com.alibaba.dubbo.common.serialize.support.json.JsonSerialization
+fastjson=com.alibaba.dubbo.common.serialize.support.json.FastJsonSerialization
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..d4eca74
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+memory=com.alibaba.dubbo.common.status.support.MemoryStatusChecker
+load=com.alibaba.dubbo.common.status.support.LoadStatusChecker
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool
new file mode 100644
index 0000000..e698e73
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool
@@ -0,0 +1,2 @@
+fixed=com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool
+cached=com.alibaba.dubbo.common.threadpool.support.cached.CachedThreadPool
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/URLTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/URLTest.java
new file mode 100644
index 0000000..ac0fe14
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/URLTest.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+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 java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.utils.CollectionUtils;
+
+/**
+ * @author ding.lid
+ * @author william.liangf
+ */
+public class URLTest {
+
+ @Test
+ public void test_valueOf_noProtocolAndHost() throws Exception {
+ URL url = URL.valueOf("/context/path?version=1.0.0&application=morgan");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+
+ url = URL.valueOf("context/path?version=1.0.0&application=morgan");
+ // ^^^^^^^ Caution , parse as host
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("context", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+ }
+
+ @Test
+ public void test_valueOf_noProtocol() throws Exception {
+ URL url = URL.valueOf("10.20.130.230");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("10.20.130.230:20880");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("10.20.130.230/context/path");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("10.20.130.230:20880/context/path");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertNull(url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+ }
+
+ @Test
+ public void test_valueOf_noHost() throws Exception {
+ URL url = URL.valueOf("file:///home/user1/router.js");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("home/user1/router.js", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ // Caution!!
+ url = URL.valueOf("file://home/user1/router.js");
+ // ^^ only tow slash!
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("home", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("user1/router.js", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+
+ url = URL.valueOf("file:/home/user1/router.js");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("home/user1/router.js", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("file:///d:/home/user1/router.js");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("d:/home/user1/router.js", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("file:///home/user1/router.js?p1=v1&p2=v2");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("home/user1/router.js", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("p1", "v1");
+ params.put("p2", "v2");
+ assertEquals(params, url.getParameters());
+
+ url = URL.valueOf("file:/home/user1/router.js?p1=v1&p2=v2");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("home/user1/router.js", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ params = new HashMap<String, String>();
+ params.put("p1", "v1");
+ params.put("p2", "v2");
+ assertEquals(params, url.getParameters());
+ }
+
+ @Test
+ public void test_valueOf_WithProtocolHost() throws Exception {
+ URL url = URL.valueOf("dubbo://10.20.130.230");
+ assertEquals("dubbo", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("dubbo://10.20.130.230:20880/context/path");
+ assertEquals("dubbo", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880?version=1.0.0");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(1, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("noValue", url.getParameter("noValue"));
+ }
+
+ @Test
+ public void test_valueOf_Exception_noProtocol() throws Exception {
+ try {
+ URL.valueOf("://1.2.3.4:8080/path");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", expected.getMessage());
+ }
+ }
+
+ @Test
+ public void test_getAddress() throws Exception {
+ URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertEquals("10.20.130.230:20880", url1.getAddress());
+ }
+
+ @Test
+ public void test_getAbsolutePath() throws Exception {
+ URL url = new URL("p1", "1.2.2.2", 33);
+ assertEquals(null, url.getAbsolutePath());
+
+ url = new URL("file", null, 90, "/home/user1/route.js");
+ assertEquals("/home/user1/route.js", url.getAbsolutePath());
+ }
+
+ @Test
+ public void test_equals() throws Exception {
+ URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("version", "1.0.0");
+ params.put("application", "morgan");
+ URL url2 = new URL("dubbo", "admin", "hello1234", "10.20.130.230", 20880, "context/path", params);
+
+ assertEquals(url1, url2);
+ }
+
+ @Test
+ public void test_toString() throws Exception {
+ URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertThat(url1.toString(), anyOf(
+ equalTo("dubbo://10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
+ equalTo("dubbo://10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
+ );
+ }
+
+ @Test
+ public void test_toFullString() throws Exception {
+ URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertThat(url1.toFullString(), anyOf(
+ equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
+ equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
+ );
+ }
+
+ @Test
+ public void test_set_methods() throws Exception {
+ URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+
+ url = url.setHost("host");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setPort(1);
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setPath("path");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setProtocol("protocol");
+
+ assertEquals("protocol", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setUsername("username");
+
+ assertEquals("protocol", url.getProtocol());
+ assertEquals("username", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setPassword("password");
+
+ assertEquals("protocol", url.getProtocol());
+ assertEquals("username", url.getUsername());
+ assertEquals("password", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+ }
+
+ @Test
+ public void test_removeParameters() throws Exception {
+ URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+
+ url = url.removeParameter("version");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+ url = url.removeParameters("version", "application");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+ url = url.removeParameters(Arrays.asList("version", "application"));
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+ }
+
+ @Test
+ public void test_addParameters() throws Exception {
+ URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameters(CollectionUtils.toStringMap("k1", "v1", "k2", "v2"));
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameters("k1", "v1", "k2", "v2", "application", "xxx");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("xxx", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParametersIfAbsent(CollectionUtils.toStringMap("k1", "v1", "k2", "v2", "application", "xxx"));
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameter("k1", "v1");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameter("application", "xxx");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(1, url.getParameters().size());
+ assertEquals("xxx", url.getParameter("application"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameterIfAbsent("application", "xxx");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(1, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ }
+
+ @Test
+ public void test_windowAbsolutePathBeginWithSlashIsValid() throws Exception {
+ final String osProperty = System.getProperties().getProperty("os.name");
+ if(!osProperty.toLowerCase().contains("windows")) return;
+
+ System.out.println("Test Windows valid path string.");
+
+ File f0 = new File("C:/Windows");
+ File f1 = new File("/C:/Windows");
+
+ File f2 = new File("C:\\Windows");
+ File f3 = new File("/C:\\Windows");
+ File f4 = new File("\\C:\\Windows");
+
+ assertEquals(f0, f1);
+ assertEquals(f0, f2);
+ assertEquals(f0, f3);
+ assertEquals(f0, f4);
+ }
+
+ @Test
+ public void test_javaNetUrl() throws Exception {
+ java.net.URL url = new java.net.URL("http://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan#anchor1");
+
+ assertEquals("http", url.getProtocol());
+ assertEquals("admin:hello1234", url.getUserInfo());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("/context/path", url.getPath());
+ assertEquals("version=1.0.0&application=morgan", url.getQuery());
+ assertEquals("anchor1", url.getRef());
+
+ assertEquals("admin:hello1234@10.20.130.230:20880", url.getAuthority());
+ assertEquals("/context/path?version=1.0.0&application=morgan", url.getFile());
+ }
+
+ @Test
+ public void test_Anyhost() throws Exception {
+ URL url = URL.valueOf("dubbo://0.0.0.0:20880");
+ assertEquals("0.0.0.0", url.getHost());
+ assertTrue(url.isAnyHost());
+ }
+
+ @Test
+ public void test_Localhost() throws Exception {
+ URL url = URL.valueOf("dubbo://127.0.0.1:20880");
+ assertEquals("127.0.0.1", url.getHost());
+ assertTrue(url.isLocalHost());
+
+ url = URL.valueOf("dubbo://127.0.1.1:20880");
+ assertEquals("127.0.1.1", url.getHost());
+ assertTrue(url.isLocalHost());
+
+ url = URL.valueOf("dubbo://localhost:20880");
+ assertEquals("localhost", url.getHost());
+ assertTrue(url.isLocalHost());
+ }
+
+}
\ 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..6f709fe
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ExtensionLoaderTest.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.ActivateExt1Impl1;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.GroupActivateExtImpl;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl1;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl2;
+import com.alibaba.dubbo.common.extensionloader.activate.impl.ValueActivateExtImpl;
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+import com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl1;
+import com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl2;
+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;
+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+import com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper1;
+import com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper2;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl2;
+import com.alibaba.dubbo.common.extensionloader.ext7.Ext7;
+import com.alibaba.dubbo.common.utils.LogUtil;
+
+/**
+ * @author ding.lid
+ */
+public class ExtensionLoaderTest {
+ @Test
+ public void test_getDefault() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getDefaultExtension();
+ assertThat(ext, instanceOf(Ext1Impl1.class));
+
+ String name = ExtensionLoader.getExtensionLoader(Ext1.class).getDefaultExtensionName();
+ assertEquals("impl1", name);
+ }
+
+ @Test
+ public void test_getDefault_NULL() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getDefaultExtension();
+ assertNull(ext);
+
+ String name = ExtensionLoader.getExtensionLoader(Ext2.class).getDefaultExtensionName();
+ assertNull(name);
+ }
+
+ @Test
+ public void test_getExtension() throws Exception {
+ assertTrue(ExtensionLoader.getExtensionLoader(Ext1.class).getExtension("impl1") instanceof Ext1Impl1);
+ assertTrue(ExtensionLoader.getExtensionLoader(Ext1.class).getExtension("impl2") instanceof Ext1Impl2);
+ }
+
+ @Test
+ public void test_getExtension_WithWrapper() throws Exception {
+ Ext5NoAdaptiveMethod impl1 = ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getExtension("impl1");
+ assertThat(impl1, anyOf(instanceOf(Ext5Wrapper1.class), instanceOf(Ext5Wrapper2.class)));
+
+ Ext5NoAdaptiveMethod impl2 = ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getExtension("impl2") ;
+ assertThat(impl2, anyOf(instanceOf(Ext5Wrapper1.class), instanceOf(Ext5Wrapper2.class)));
+
+
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+ int echoCount1 = Ext5Wrapper1.echoCount.get();
+ int echoCount2 = Ext5Wrapper2.echoCount.get();
+ int yellCount1 = Ext5Wrapper1.yellCount.get();
+ int yellCount2 = Ext5Wrapper2.yellCount.get();
+
+ assertEquals("Ext5Impl1-echo", impl1.echo(url, "ha"));
+ assertEquals(echoCount1 + 1, Ext5Wrapper1.echoCount.get());
+ assertEquals(echoCount2 + 1, Ext5Wrapper2.echoCount.get());
+ assertEquals(yellCount1, Ext5Wrapper1.yellCount.get());
+ assertEquals(yellCount2, Ext5Wrapper2.yellCount.get());
+
+ assertEquals("Ext5Impl2-yell", impl2.yell(url, "ha"));
+ assertEquals(echoCount1 + 1, Ext5Wrapper1.echoCount.get());
+ assertEquals(echoCount2 + 1, Ext5Wrapper2.echoCount.get());
+ assertEquals(yellCount1 + 1, Ext5Wrapper1.yellCount.get());
+ assertEquals(yellCount2 + 1, Ext5Wrapper2.yellCount.get());
+ }
+
+ @Test
+ public void test_getExtension_ExceptionNoExtension() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext1.class).getExtension("XXX");
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("No such extension com.alibaba.dubbo.common.extensionloader.ext1.Ext1 by name XXX"));
+ }
+ }
+
+ @Test
+ public void test_getExtension_ExceptionNoExtension_NameOnWrapperNoAffact() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getExtension("XXX");
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("No such extension com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod by name XXX"));
+ }
+ }
+
+ @Test
+ public void test_getExtension_ExceptionNullArg() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext1.class).getExtension(null);
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(), containsString("Extension name == null"));
+ }
+ }
+
+ @Test
+ public void test_hasExtension() throws Exception {
+ assertTrue(ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension("impl1"));
+ assertFalse(ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension("impl1,impl2"));
+ assertFalse(ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension("xxx"));
+
+ try {
+ ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension(null);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(), containsString("Extension name == null"));
+ }
+ }
+
+ @Test
+ public void test_getSupportedExtensions() throws Exception {
+ Set<String> exts = ExtensionLoader.getExtensionLoader(Ext1.class).getSupportedExtensions();
+
+ Set<String> expected = new HashSet<String>();
+ expected.add("impl1");
+ expected.add("impl2");
+ expected.add("impl3");
+
+ assertEquals(expected, exts);
+ }
+
+ @Test
+ public void test_getSupportedExtensions_NoExtension() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(ExtensionLoaderTest.class).getSupportedExtensions();
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(),
+ allOf(containsString("com.alibaba.dubbo.common.extensionloader.ExtensionLoaderTest"),
+ containsString("is not extension"),
+ containsString("WITHOUT @SPI Annotation")));
+
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_defaultExtension() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ String echo = ext.echo(url, "haha");
+ assertEquals("Ext1Impl1-echo", echo);
+ }
+
+ @Test
+ public void test_getAdaptiveExtension() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("ext1", "impl2");
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ String echo = ext.echo(url, "haha");
+ assertEquals("Ext1Impl2-echo", echo);
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_customizeKey() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("key2", "impl2");
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ String echo = ext.yell(url, "haha");
+ assertEquals("Ext1Impl2-yell", echo);
+
+ url = url.addParameter("key1", "impl3"); // 注意: URL是值类型
+ echo = ext.yell(url, "haha");
+ assertEquals("Ext1Impl3-yell", echo);
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_UrlNpe() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ try {
+ ext.echo(null, "haha");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("url == null", e.getMessage());
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_ExceptionWhenNoAdativeMethodOnInterface() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getAdaptiveExtension();
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(),
+ allOf(containsString("Can not create adaptive extenstion interface com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod"),
+ containsString("No adaptive method on extension com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod, refuse to create the adaptive class")));
+ }
+ // 多次get,都会报错且相同
+ try {
+ ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getAdaptiveExtension();
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(),
+ allOf(containsString("Can not create adaptive extenstion interface com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod"),
+ containsString("No adaptive method on extension com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod, refuse to create the adaptive class")));
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_ExceptionWhenNotAdativeMethod() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ try {
+ ext.bang(url, 33);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ assertThat(expected.getMessage(), containsString("method "));
+ assertThat(
+ expected.getMessage(),
+ containsString("of interface com.alibaba.dubbo.common.extensionloader.ext1.Ext1 is not adaptive method!"));
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_ExceptionWhenNoUrlAttrib() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext4.class).getAdaptiveExtension();
+ fail();
+ } catch (Exception expected) {
+ assertThat(expected.getMessage(), containsString("fail to create adative class for interface "));
+ assertThat(expected.getMessage(), containsString(": not found url parameter or url attribute in parameters of method "));
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_protocolKey() throws Exception {
+ Ext3 ext = ExtensionLoader.getExtensionLoader(Ext3.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("impl3", "1.2.3.4", 1010, "path1", map);
+
+ String echo = ext.echo(url, "s");
+ assertEquals("Ext3Impl3-echo", echo);
+
+ url = url.addParameter("key1", "impl2");
+ echo = ext.echo(url, "s");
+ assertEquals("Ext3Impl2-echo", echo);
+
+ String yell = ext.yell(url, "d");
+ assertEquals("Ext3Impl3-yell", yell);
+ }
+
+
+ @Test
+ public void test_getAdaptiveExtension_lastProtocolKey() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("impl1", "1.2.3.4", 1010, "path1", map);
+ String yell = ext.yell(url, "s");
+ assertEquals("Ext2Impl1-yell", yell);
+
+ url = url.addParameter("key1", "impl2");
+ yell = ext.yell(url, "s");
+ assertEquals("Ext2Impl2-yell", yell);
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("ext2", "impl1");
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ UrlHolder holder = new UrlHolder();
+ holder.setUrl(url);
+
+ String echo = ext.echo(holder, "haha");
+ assertEquals("Ext2Impl1-echo", echo);
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension_noExtension() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+
+ UrlHolder holder = new UrlHolder();
+ holder.setUrl(url);
+
+ try {
+ ext.echo(holder, "haha");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Fail to get extension("));
+ }
+
+ url = url.addParameter("ext2", "XXX");
+ holder.setUrl(url);
+ try {
+ ext.echo(holder, "haha");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("No such extension"));
+ }
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension_UrlNpe() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ try {
+ ext.echo(null, "haha");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder argument == null", e.getMessage());
+ }
+
+ try {
+ ext.echo(new UrlHolder(), "haha");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder argument getUrl() == null", e.getMessage());
+ }
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension_ExceptionWhenNotAdativeMethod() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ try {
+ ext.bang(url, 33);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ assertThat(expected.getMessage(), containsString("method "));
+ assertThat(
+ expected.getMessage(),
+ containsString("of interface com.alibaba.dubbo.common.extensionloader.ext2.Ext2 is not adaptive method!"));
+ }
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension_ExceptionWhenNameNotProvided() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+
+ UrlHolder holder = new UrlHolder();
+ holder.setUrl(url);
+
+ try {
+ ext.echo(holder, "impl1");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Fail to get extension("));
+ }
+
+ url = url.addParameter("key1", "impl1");
+ holder.setUrl(url);
+ try {
+ ext.echo(holder, "haha");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext2.Ext2) name from url"));
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_inject() throws Exception {
+ LogUtil.start();
+ Ext6 ext = ExtensionLoader.getExtensionLoader(Ext6.class).getAdaptiveExtension();
+
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+ url = url.addParameters("ext6", "impl1");
+
+ assertEquals("Ext6Impl1-echo-Ext1Impl1-echo", ext.echo(url, "ha"));
+
+ Assert.assertTrue("can not find error.", LogUtil.checkNoError());
+ LogUtil.stop();
+
+ url = url.addParameters("ext1", "impl2");
+ assertEquals("Ext6Impl1-echo-Ext1Impl2-echo", ext.echo(url, "ha"));
+
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_InjectNotExtFail() throws Exception {
+ Ext6 ext = ExtensionLoader.getExtensionLoader(Ext6.class).getExtension("impl2");
+
+ Ext6Impl2 impl = (Ext6Impl2) ext;
+ assertNull(impl.getList());
+ }
+
+ @Test
+ public void test_InitError() throws Exception {
+ ExtensionLoader<Ext7> loader = ExtensionLoader.getExtensionLoader(Ext7.class);
+
+ loader.getExtension("ok");
+
+ try {
+ loader.getExtension("error");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Failed to load extension class(interface: interface com.alibaba.dubbo.common.extensionloader.ext7.Ext7"));
+ assertThat(expected.getCause(), instanceOf(ExceptionInInitializerError.class));
+ }
+ }
+
+ @Test
+ public void testLoadActivateExtension() throws Exception {
+ // test default
+ URL url = URL.valueOf("test://localhost/test");
+ List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+ .getActivateExtension(url, new String[]{}, "default_group");
+ Assert.assertEquals(1, list.size());
+ Assert.assertTrue(list.get(0).getClass() == ActivateExt1Impl1.class);
+
+ // test group
+ url = url.addParameter(Constants.GROUP_KEY, "group1");
+ list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+ .getActivateExtension(url, new String[]{}, "group1");
+ Assert.assertEquals(1, list.size());
+ Assert.assertTrue(list.get(0).getClass() == GroupActivateExtImpl.class);
+
+ // test value
+ url = url.removeParameter(Constants.GROUP_KEY);
+ url = url.addParameter(Constants.GROUP_KEY, "value");
+ url = url.addParameter("value", "value");
+ list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+ .getActivateExtension(url, new String[]{}, "value");
+ Assert.assertEquals(1, list.size());
+ Assert.assertTrue(list.get(0).getClass() == ValueActivateExtImpl.class);
+
+ // test order
+ url = URL.valueOf("test://localhost/test");
+ url = url.addParameter(Constants.GROUP_KEY, "order");
+ list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+ .getActivateExtension(url, new String[]{}, "order");
+ Assert.assertEquals(2, list.size());
+ Assert.assertTrue(list.get(0).getClass() == OrderActivateExtImpl1.class);
+ Assert.assertTrue(list.get(1).getClass() == OrderActivateExtImpl2.class);
+ }
+
+ @Test
+ public void testLoadDefaultActivateExtension() throws Exception {
+ // test default
+ URL url = URL.valueOf("test://localhost/test?ext=order1,default");
+ List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+ .getActivateExtension(url, "ext", "default_group");
+ Assert.assertEquals(2, list.size());
+ Assert.assertTrue(list.get(0).getClass() == OrderActivateExtImpl1.class);
+ Assert.assertTrue(list.get(1).getClass() == ActivateExt1Impl1.class);
+
+ url = URL.valueOf("test://localhost/test?ext=default,order1");
+ list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+ .getActivateExtension(url, "ext", "default_group");
+ Assert.assertEquals(2, list.size());
+ Assert.assertTrue(list.get(0).getClass() == ActivateExt1Impl1.class);
+ Assert.assertTrue(list.get(1).getClass() == OrderActivateExtImpl1.class);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/ActivateExt1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/ActivateExt1.java
new file mode 100644
index 0000000..d8b78f3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/ActivateExt1.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.common.extensionloader.activate;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@SPI("impl1")
+public interface ActivateExt1 {
+ String echo(String msg);
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ActivateExt1Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ActivateExt1Impl1.java
new file mode 100644
index 0000000..577d6c7
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ActivateExt1Impl1.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(group = {"default_group"})
+public class ActivateExt1Impl1 implements ActivateExt1 {
+ public String echo(String msg) {
+ return msg;
+ }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/GroupActivateExtImpl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/GroupActivateExtImpl.java
new file mode 100644
index 0000000..d46ebf0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/GroupActivateExtImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(group = {"group1", "group2"})
+public class GroupActivateExtImpl implements ActivateExt1 {
+
+ public String echo(String msg) {
+ return msg;
+ }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl1.java
new file mode 100644
index 0000000..544041e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl1.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(order = 1, group = {"order"})
+public class OrderActivateExtImpl1 implements ActivateExt1 {
+
+ public String echo(String msg) {
+ return msg;
+ }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl2.java
new file mode 100644
index 0000000..3d9dd94
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/OrderActivateExtImpl2.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(order = 2, group = {"order"})
+public class OrderActivateExtImpl2 implements ActivateExt1 {
+
+ public String echo(String msg) {
+ return msg;
+ }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ValueActivateExtImpl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ValueActivateExtImpl.java
new file mode 100644
index 0000000..14328e6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/activate/impl/ValueActivateExtImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.common.extensionloader.activate.impl;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(value = {"value"}, group = {"value"})
+public class ValueActivateExtImpl implements ActivateExt1 {
+
+ public String echo(String msg) {
+ return msg;
+ }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/Ext1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/Ext1.java
new file mode 100644
index 0000000..fcc9ba3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/Ext1.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext1;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * @author ding.lid
+ */
+@SPI("impl1")
+public interface Ext1 {
+ @Adaptive
+ String echo(URL url, String s);
+
+ @Adaptive({"key1", "key2"})
+ String yell(URL url, String s);
+
+ String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl1.java
new file mode 100644
index 0000000..3524440
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl1.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext1.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+
+/**
+ * @author ding.lid
+ */
+@Extension("impl1")
+public class Ext1Impl1 implements Ext1 {
+ public String echo(URL url, String s) {
+ return "Ext1Impl1-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext1Impl1-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang1";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.java
new file mode 100644
index 0000000..12e9265
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext1.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext1Impl2 implements Ext1 {
+ public String echo(URL url, String s) {
+ return "Ext1Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext1Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang2";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.java
new file mode 100644
index 0000000..86e18de
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext1.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext1Impl3 implements Ext1 {
+ public String echo(URL url, String s) {
+ return "Ext1Impl3-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext1Impl3-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang3";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.java
new file mode 100644
index 0000000..7978dcd
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * 无Default
+ *
+ * @author ding.lid
+ */
+@SPI
+public interface Ext2 {
+ @Adaptive
+ String echo(UrlHolder holder, String s);
+
+ @Adaptive({"key1", "protocol"})
+ String yell(URL url, String s);
+
+ String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/UrlHolder.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/UrlHolder.java
new file mode 100644
index 0000000..4c8aa82
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/UrlHolder.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class UrlHolder {
+ private Double Num;
+
+ private URL url;
+
+ private String name;
+
+ private int age;
+
+ public Double getNum() {
+ return Num;
+ }
+
+ public void setNum(Double num) {
+ Num = num;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.java
new file mode 100644
index 0000000..5eb0b8c
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;
+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;
+
+/**
+ * @author ding.lid
+ */
+public class Ext2Impl1 implements Ext2 {
+ public String echo(UrlHolder holder, String s) {
+ return "Ext2Impl1-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext2Impl1-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang1";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.java
new file mode 100644
index 0000000..3597345
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;
+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext2Impl2 implements Ext2 {
+ public String echo(UrlHolder holder, String s) {
+ return "Ext2Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext2Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang2";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.java
new file mode 100644
index 0000000..8f1805e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;
+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext2Impl3 implements Ext2 {
+ public String echo(UrlHolder holder, String s) {
+ return "Ext2Impl3-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext2Impl3-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang3";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/Ext3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/Ext3.java
new file mode 100644
index 0000000..38bec13
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/Ext3.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext3;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * @author ding.lid
+ */
+@SPI("impl3")
+public interface Ext3 {
+ @Adaptive({"key1", "protocol"})
+ String echo(URL url, String s);
+
+ @Adaptive({"protocol", "key2"})
+ String yell(URL url, String s);
+
+ String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.java
new file mode 100644
index 0000000..9d8e29b
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+
+/**
+ * @author ding.lid
+ */
+public class Ext3Impl1 implements Ext3 {
+ public String echo(URL url, String s) {
+ return "Ext3Impl3-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext3Impl3-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang1";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.java
new file mode 100644
index 0000000..5953c93
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext3Impl2 implements Ext3 {
+ public String echo(URL url, String s) {
+ return "Ext3Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext3Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang2";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.java
new file mode 100644
index 0000000..ae5b6d1
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext3Impl3 implements Ext3 {
+ public String echo(URL url, String s) {
+ return "Ext3Impl3-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext3Impl3-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang3";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/Ext4.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/Ext4.java
new file mode 100644
index 0000000..b255b9b
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/Ext4.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext4;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * @author ding.lid
+ */
+@SPI("impl1")
+public interface Ext4 {
+ @Adaptive
+ String bark(String name, List<Object> list); // 没有URL参数的方法
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.java
new file mode 100644
index 0000000..6e72d8d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext4.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;
+
+/**
+ * @author ding.lid
+ */
+public class Ext4Impl1 implements Ext4 {
+ public String bark(String name, List<Object> list) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.java
new file mode 100644
index 0000000..bf98111
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext4.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext4Impl2 implements Ext4 {
+ public String echo(URL url, String s) {
+ return "Ext3Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext3Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang2";
+ }
+
+ public String bark(String name, List<Object> list) {
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/Ext5NoAdaptiveMethod.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/Ext5NoAdaptiveMethod.java
new file mode 100644
index 0000000..955a4b1
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/Ext5NoAdaptiveMethod.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * @author ding.lid
+ */
+@SPI("impl1")
+public interface Ext5NoAdaptiveMethod {
+ String echo(URL url, String s);
+
+ String yell(URL url, String s);
+
+ String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.java
new file mode 100644
index 0000000..f674af0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ */
+public class Ext5Impl1 implements Ext5NoAdaptiveMethod {
+ public String echo(URL url, String s) {
+ return "Ext5Impl1-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext5Impl1-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "impl1";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.java
new file mode 100644
index 0000000..a6c6cda
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Ext5Impl2 implements Ext5NoAdaptiveMethod {
+ public String echo(URL url, String s) {
+ return "Ext5Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext5Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "impl2";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java
new file mode 100644
index 0000000..a151777
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5.impl;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ */
+public class Ext5Wrapper1 implements Ext5NoAdaptiveMethod {
+ Ext5NoAdaptiveMethod instance;
+
+ public static AtomicInteger echoCount = new AtomicInteger();
+ public static AtomicInteger yellCount = new AtomicInteger();
+ public static AtomicInteger bangCount = new AtomicInteger();
+
+ public Ext5Wrapper1(Ext5NoAdaptiveMethod instance) {
+ this.instance = instance;
+ }
+
+ public String echo(URL url, String s) {
+ echoCount.incrementAndGet();
+ return instance.echo(url, s);
+ }
+
+ public String yell(URL url, String s) {
+ yellCount.incrementAndGet();
+ return instance.yell(url, s);
+ }
+
+ public String bang(URL url, int i) {
+ bangCount.incrementAndGet();
+ return instance.bang(url, i);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper2.java
new file mode 100644
index 0000000..ffb1dcb
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper2.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5.impl;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ */
+public class Ext5Wrapper2 implements Ext5NoAdaptiveMethod {
+ Ext5NoAdaptiveMethod instance;
+
+ public static AtomicInteger echoCount = new AtomicInteger();
+ public static AtomicInteger yellCount = new AtomicInteger();
+ public static AtomicInteger bangCount = new AtomicInteger();
+
+ public Ext5Wrapper2(Ext5NoAdaptiveMethod instance) {
+ this.instance = instance;
+ }
+
+ public String echo(URL url, String s) {
+ echoCount.incrementAndGet();
+ return instance.echo(url, s);
+ }
+
+ public String yell(URL url, String s) {
+ yellCount.incrementAndGet();
+ return instance.yell(url, s);
+ }
+
+ public String bang(URL url, int i) {
+ bangCount.incrementAndGet();
+ return instance.bang(url, i);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Dao.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Dao.java
new file mode 100644
index 0000000..9884b6b
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Dao.java
@@ -0,0 +1,6 @@
+package com.alibaba.dubbo.common.extensionloader.ext6_inject;
+
+
+public interface Dao {
+ public void update();
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Ext6.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Ext6.java
new file mode 100644
index 0000000..2562e57
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Ext6.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext6_inject;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * 无Default
+ *
+ * @author ding.lid
+ */
+@SPI
+public interface Ext6 {
+ @Adaptive
+ String echo(URL url, String s);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/DaoImpl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/DaoImpl.java
new file mode 100644
index 0000000..89a7cde
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/DaoImpl.java
@@ -0,0 +1,9 @@
+package com.alibaba.dubbo.common.extensionloader.ext6_inject.impl;
+
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Dao;
+
+public class DaoImpl implements Dao {
+ public void update(){
+
+ }
+}
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.java
new file mode 100644
index 0000000..d3a64f1
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext6_inject.impl;
+
+import junit.framework.Assert;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Dao;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;
+
+/**
+ * @author ding.lid
+ */
+public class Ext6Impl1 implements Ext6 {
+ Ext1 ext1;
+ public Dao obj;
+
+ public void setDao(Dao obj){
+ Assert.assertNotNull("inject extension instance can not be null", obj);
+ Assert.fail();
+ }
+
+ public void setExt1(Ext1 ext1) {
+ this.ext1 = ext1;
+ }
+
+ public String echo(URL url, String s) {
+ return "Ext6Impl1-echo-" + ext1.echo(url, s);
+ }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.java
new file mode 100644
index 0000000..4b93767
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext6_inject.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;
+
+/**
+ * @author ding.lid
+ */
+public class Ext6Impl2 implements Ext6 {
+ List<String> list;
+
+ public List<String> getList() {
+ return list;
+ }
+
+ public void setList(List<String> list) {
+ this.list = list;
+ }
+
+ public String echo(URL url, String s) {
+ throw new UnsupportedOperationException();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/Ext7.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/Ext7.java
new file mode 100644
index 0000000..5766719
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/Ext7.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext7;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * 用于测试:
+ * DUBBO-144 扩展点加载失败(如依赖的三方库运行时没有),如扩展点没有用到,则加载不要报错(在使用到时报错)
+ *
+ * @author ding.lid
+ */
+@SPI
+public interface Ext7 {
+ @Adaptive
+ String echo(URL url, String s);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7Impl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7Impl.java
new file mode 100644
index 0000000..3eedd08
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7Impl.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext7.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext7.Ext7;
+
+/**
+ * @author ding.lid
+ */
+public class Ext7Impl implements Ext7 {
+ public String echo(URL url, String s) {
+ return "";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7InitErrorImpl.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7InitErrorImpl.java
new file mode 100644
index 0000000..0edba0e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext7/impl/Ext7InitErrorImpl.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext7.impl;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext7.Ext7;
+
+/**
+ * @author ding.lid
+ */
+public class Ext7InitErrorImpl implements Ext7 {
+
+ static {
+ if(true) {
+ throw new RuntimeException("intended!");
+ }
+ }
+
+ public String echo(URL url, String s) {
+ return "";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/BytesTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/BytesTest.java
new file mode 100644
index 0000000..8e6c97d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/BytesTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+import org.junit.Assert;
+
+import junit.framework.TestCase;
+
+public class BytesTest extends TestCase
+{
+ private static final byte[] b1 = "adpfioha;eoh;aldfadl;kfadslkfdajfio123431241235123davas;odvwe;lmzcoqpwoewqogineopwqihwqetup\n\tejqf;lajsfd中文字符0da0gsaofdsf==adfasdfs".getBytes();
+
+ public void testMain() throws Exception
+ {
+ short s = (short)0xabcd;
+ assertEquals(s, Bytes.bytes2short(Bytes.short2bytes(s)) );
+
+ int i = 198284;
+ assertEquals(i, Bytes.bytes2int(Bytes.int2bytes(i)) );
+
+ long l = 13841747174l;
+ assertEquals(l, Bytes.bytes2long(Bytes.long2bytes(l)) );
+
+ float f = 1.3f;
+ assertEquals(f, Bytes.bytes2float(Bytes.float2bytes(f)) );
+
+ double d = 11213.3;
+ assertEquals(d, Bytes.bytes2double(Bytes.double2bytes(d)) );
+
+ assertSame(Bytes.int2bytes(i), int2bytes(i));
+ assertSame(Bytes.long2bytes(l), long2bytes(l));
+ }
+
+ public void testBase64() throws Exception
+ {
+ String str = Bytes.bytes2base64(b1);
+ byte[] b2 = Bytes.base642bytes(str);
+ assertSame(b1, b2);
+ }
+
+ static byte[] bytes1 = {3,12,14,41,12,2,3,12,4,67,23};
+ static byte[] bytes2 = {3,12,14,41,12,2,3,12,4,67};
+ // 把失败的情况,失败Case加的防护网:
+ // 当有填充字符时,会失败!
+ public void testBase64_s2b2s_FailCaseLog() throws Exception {
+ String s1 = Bytes.bytes2base64(bytes1);
+ byte[] out1 = Bytes.base642bytes(s1);
+ Assert.assertArrayEquals(bytes1, out1);
+
+
+ String s2 = Bytes.bytes2base64(bytes2);
+ byte[] out2 = Bytes.base642bytes(s2);
+ Assert.assertArrayEquals(bytes2, out2);
+ }
+
+ public void testHex() throws Exception
+ {
+ String str = Bytes.bytes2hex(b1);
+ assertSame(b1, Bytes.hex2bytes(str));
+ }
+
+ private static void assertSame(byte[] b1, byte[] b2)
+ {
+ assertEquals(b1.length, b2.length);
+ for(int i=0;i<b1.length;i++)
+ assertEquals(b1[i], b2[i]);
+ }
+
+ // tb-remoting codec method.
+ static public byte[] int2bytes(int x) {
+ byte[] bb = new byte[4];
+ bb[0] = (byte) (x >> 24);
+ bb[1] = (byte) (x >> 16);
+ bb[2] = (byte) (x >> 8);
+ bb[3] = (byte) (x >> 0);
+ return bb;
+ }
+
+ static public int bytes2int(byte[] bb, int idx) {
+ return ((bb[idx + 0] & 0xFF) << 24)
+ | ((bb[idx + 1] & 0xFF) << 16)
+ | ((bb[idx + 2] & 0xFF) << 8)
+ | ((bb[idx + 3] & 0xFF) << 0);
+ }
+
+ static public byte[] long2bytes(long x) {
+ byte[] bb = new byte[8];
+ bb[0] = (byte) (x >> 56);
+ bb[1] = (byte) (x >> 48);
+ bb[2] = (byte) (x >> 40);
+ bb[3] = (byte) (x >> 32);
+ bb[4] = (byte) (x >> 24);
+ bb[5] = (byte) (x >> 16);
+ bb[6] = (byte) (x >> 8);
+ bb[7] = (byte) (x >> 0);
+ return bb;
+ }
+
+ static public long bytes2long(byte[] bb, int idx) {
+ return (((long) bb[idx + 0] & 0xFF) << 56)
+ | (((long) bb[idx + 1] & 0xFF) << 48)
+ | (((long) bb[idx + 2] & 0xFF) << 40)
+ | (((long) bb[idx + 3] & 0xFF) << 32)
+ | (((long) bb[idx + 4] & 0xFF) << 24)
+ | (((long) bb[idx + 5] & 0xFF) << 16)
+ | (((long) bb[idx + 6] & 0xFF) << 8)
+ | (((long) bb[idx + 7] & 0xFF) << 0);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/StreamUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/StreamUtilsTest.java
new file mode 100644
index 0000000..71233e0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/io/StreamUtilsTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author ding.lid
+ */
+public class StreamUtilsTest {
+
+ @Test
+ public void testMarkSupportedInputStream() throws Exception {
+ InputStream is = StreamUtilsTest.class.getResourceAsStream("/StreamUtilsTest.txt");
+ assertEquals(10, is.available());
+
+ is = new PushbackInputStream(is);
+ assertEquals(10, is.available());
+ assertFalse(is.markSupported());
+
+ is = StreamUtils.markSupportedInputStream(is);
+ assertEquals(10, is.available());
+
+ is.mark(0);
+ assertEquals((int) '0', is.read());
+ assertEquals((int) '1', is.read());
+
+ is.reset();
+ assertEquals((int) '0', is.read());
+ assertEquals((int) '1', is.read());
+ assertEquals((int) '2', is.read());
+
+ is.mark(0);
+ assertEquals((int) '3', is.read());
+ assertEquals((int) '4', is.read());
+ assertEquals((int) '5', is.read());
+
+ is.reset();
+ assertEquals((int) '3', is.read());
+ assertEquals((int) '4', is.read());
+
+ is.mark(0);
+ assertEquals((int) '5', is.read());
+ assertEquals((int) '6', is.read());
+
+ is.reset();
+ assertEquals((int) '5', is.read());
+ assertEquals((int) '6', is.read());
+ assertEquals((int) '7', is.read());
+ assertEquals((int) '8', is.read());
+ assertEquals((int) '9', is.read());
+ assertEquals(-1, is.read());
+ assertEquals(-1, is.read());
+
+ is.mark(0);
+ assertEquals(-1, is.read());
+ assertEquals(-1, is.read());
+
+ is.reset();
+ assertEquals(-1, is.read());
+ assertEquals(-1, is.read());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONReaderTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONReaderTest.java
new file mode 100644
index 0000000..b3330d6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONReaderTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import com.alibaba.dubbo.common.io.UnsafeStringReader;
+
+import junit.framework.TestCase;
+
+public class JSONReaderTest extends TestCase
+{
+ public void testMain() throws Exception
+ {
+ String json = "{ name: 'name', friends: [ 1, null, 3.2, ] }";
+ JSONReader reader = new JSONReader(new UnsafeStringReader(json));
+ assertEquals(reader.nextToken().type, JSONToken.LBRACE);
+ assertEquals(reader.nextToken().type, JSONToken.IDENT);
+ assertEquals(reader.nextToken().type, JSONToken.COLON);
+ assertEquals(reader.nextToken().type, JSONToken.STRING);
+ assertEquals(reader.nextToken().type, JSONToken.COMMA);
+ assertEquals(reader.nextToken().type, JSONToken.IDENT);
+ assertEquals(reader.nextToken().type, JSONToken.COLON);
+ assertEquals(reader.nextToken().type, JSONToken.LSQUARE);
+ assertEquals(reader.nextToken().type, JSONToken.INT);
+ assertEquals(reader.nextToken().type, JSONToken.COMMA);
+ assertEquals(reader.nextToken().type, JSONToken.NULL);
+ assertEquals(reader.nextToken().type, JSONToken.COMMA);
+ assertEquals(reader.nextToken().type, JSONToken.FLOAT);
+ assertEquals(reader.nextToken().type, JSONToken.COMMA);
+ assertEquals(reader.nextToken().type, JSONToken.RSQUARE);
+ assertEquals(reader.nextToken().type, JSONToken.RBRACE);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONTest.java
new file mode 100644
index 0000000..337dba7
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONTest.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+public class JSONTest extends TestCase
+{
+ public void testException() throws Exception {
+ MyException e = new MyException("001", "AAAAAAAA");
+
+ StringWriter writer = new StringWriter();
+ JSON.json(e, writer);
+ String json = writer.getBuffer().toString();
+ System.out.println(json);
+ // Assert.assertEquals("{\"code\":\"001\",\"message\":\"AAAAAAAA\"}", json);
+
+ StringReader reader = new StringReader(json);
+ MyException result = JSON.parse(reader, MyException.class);
+ Assert.assertEquals("001", result.getCode());
+ Assert.assertEquals("AAAAAAAA", result.getMessage());
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testMap() throws Exception {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("aaa", "bbb");
+
+ StringWriter writer = new StringWriter();
+ JSON.json(map, writer);
+ String json = writer.getBuffer().toString();
+ Assert.assertEquals("{\"aaa\":\"bbb\"}", json);
+
+ StringReader reader = new StringReader(json);
+ Map<String, String> result = JSON.parse(reader, Map.class);
+ Assert.assertEquals("bbb", result.get("aaa"));
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testMapArray() throws Exception {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("aaa", "bbb");
+
+ StringWriter writer = new StringWriter();
+ JSON.json(new Object[] {map}, writer); // args
+ String json = writer.getBuffer().toString();
+ Assert.assertEquals("[{\"aaa\":\"bbb\"}]", json);
+
+ StringReader reader = new StringReader(json);
+ Object[] result = JSON.parse(reader, new Class<?>[] { Map.class });
+ Assert.assertEquals(1, result.length);
+ Assert.assertEquals("bbb", ((Map<String, String>)result[0]).get("aaa"));
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testLinkedMap() throws Exception {
+ LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
+ map.put("aaa", "bbb");
+
+ StringWriter writer = new StringWriter();
+ JSON.json(map, writer);
+ String json = writer.getBuffer().toString();
+ Assert.assertEquals("{\"aaa\":\"bbb\"}", json);
+
+ StringReader reader = new StringReader(json);
+ LinkedHashMap<String, String> result = JSON.parse(reader, LinkedHashMap.class);
+ Assert.assertEquals("bbb", result.get("aaa"));
+ }
+
+ public void testObject2Json() throws Exception
+ {
+ Bean bean = new Bean();
+ bean.array = new int[]{1, 3, 4};
+ bean.setName("ql");
+
+ String json = JSON.json(bean);
+ bean = JSON.parse(json, Bean.class);
+ assertEquals(bean.getName(), "ql");
+ assertEquals(bean.getDisplayName(), "钱磊");
+ assertEquals(bean.bytes.length, DEFAULT_BYTES.length);
+ assertEquals(bean.$$, DEFAULT_$$);
+
+ assertEquals("{\"name\":\"ql\",\"array\":[1,3,4]}", JSON.json(bean, new String[]{"name", "array"}));
+ }
+
+ public void testParse2JSONObject() throws Exception
+ {
+ JSONObject jo = (JSONObject)JSON.parse("{name:'qianlei',array:[1,2,3,4,98.123],b1:TRUE,$1:NULL,$2:FALSE,__3:NULL}");
+ assertEquals(jo.getString("name"), "qianlei");
+ assertEquals(jo.getArray("array").length(), 5);
+ assertEquals(jo.get("$2"), Boolean.FALSE);
+ assertEquals(jo.get("__3"), null);
+
+ for(int i=0;i<10000;i++)
+ JSON.parse("{\"name\":\"qianlei\",\"array\":[1,2,3,4,98.123],\"displayName\":\"钱磊\"}");
+
+ long now = System.currentTimeMillis();
+ for(int i=0;i<10000;i++)
+ JSON.parse("{\"name\":\"qianlei\",\"array\":[1,2,3,4,98.123],\"displayName\":\"钱磊\"}");
+ System.out.println("parse to JSONObject 10000 times in: " + ( System.currentTimeMillis()-now) );
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testParse2Class() throws Exception
+ {
+ int[] o1 = {1,2,3,4,5}, o2 = JSON.parse("[1.2,2,3,4,5]", int[].class);
+ assertEquals(o2.length, 5);
+ for(int i=0;i<5;i++)
+ assertEquals(o1[i], o2[i]);
+
+ List l1 = (List)JSON.parse("[1.2,2,3,4,5]", List.class);
+ assertEquals(l1.size(), 5);
+ for(int i=0;i<5;i++)
+ assertEquals(o1[i], ((Number)l1.get(i)).intValue());
+
+ Bean bean = JSON.parse("{name:'qianlei',array:[1,2,3,4,98.123],displayName:'钱磊',$$:214726,$b:TRUE}", Bean.class);
+ assertEquals(bean.getName(), "qianlei");
+ assertEquals(bean.getDisplayName(), "钱磊");
+ assertEquals(bean.array.length, 5);
+ assertEquals(bean.$$, 214726);
+ assertEquals(bean.$b, true);
+
+ for(int i=0;i<10000;i++)
+ JSON.parse("{name:'qianlei',array:[1,2,3,4,98.123],displayName:'钱磊'}", Bean1.class);
+
+ long now = System.currentTimeMillis();
+ for(int i=0;i<10000;i++)
+ JSON.parse("{name:'qianlei',array:[1,2,3,4,98.123],displayName:'钱磊'}", Bean1.class);
+ System.out.println("parse to Class 10000 times in: " + ( System.currentTimeMillis()-now) );
+ }
+
+ public void testParse2Arguments() throws Exception
+ {
+ Object[] test = JSON.parse("[1.2, 2, {name:'qianlei',array:[1,2,3,4,98.123]} ]", new Class<?>[]{ int.class, int.class, Bean.class });
+ assertEquals(test[1], 2);
+ assertEquals(test[2].getClass(), Bean.class);
+ test = JSON.parse("[1.2, 2]", new Class<?>[]{ int.class, int.class });
+ assertEquals(test[0], 1);
+ }
+
+ static byte[] DEFAULT_BYTES = {3,12,14,41,12,2,3,12,4,67,23};
+
+ static int DEFAULT_$$ = 152;
+
+ public static class Bean1
+ {
+ private String name,displayName;
+
+ public int[] array;
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ public static class Bean
+ {
+ private String name, displayName = "钱磊";
+
+ public int[] array;
+
+ public boolean $b;
+
+ public int $$ = DEFAULT_$$;
+
+ public byte[] bytes = DEFAULT_BYTES;
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONWriterTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONWriterTest.java
new file mode 100644
index 0000000..16a71e8
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/JSONWriterTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+public class JSONWriterTest extends TestCase
+{
+ public void testWriteJson() throws Exception
+ {
+ StringWriter w = new StringWriter();
+ JSONWriter writer = new JSONWriter(w);
+
+ writer.valueNull();
+ assertEquals(w.getBuffer().toString(), "null");
+
+ // write array.
+ w.getBuffer().setLength(0);
+ writer.arrayBegin().valueNull().valueBoolean(false).valueInt(16).arrayEnd();
+ assertEquals(w.getBuffer().toString(),"[null,false,16]");
+
+ // write object.
+ w.getBuffer().setLength(0);
+ writer.objectBegin().objectItem("type").valueString("com.alibaba.dubbo.TestService").objectItem("version").valueString("1.1.0").objectEnd();
+ assertEquals(w.getBuffer().toString(),"{\"type\":\"com.alibaba.dubbo.TestService\",\"version\":\"1.1.0\"}");
+
+ w.getBuffer().setLength(0);
+ writer.objectBegin();
+ writer.objectItem("name").objectItem("displayName");
+ writer.objectItem("emptyList").arrayBegin().arrayEnd();
+ writer.objectItem("list").arrayBegin().valueNull().valueBoolean(false).valueInt(16).valueString("stri'''ng").arrayEnd();
+ writer.objectItem("service").objectBegin().objectItem("type").valueString("com.alibaba.dubbo.TestService").objectItem("version").valueString("1.1.0").objectEnd();
+ writer.objectEnd();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/MyException.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/MyException.java
new file mode 100644
index 0000000..178916d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/json/MyException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+public class MyException extends Exception {
+
+ private static final long serialVersionUID = 2905707783883694687L;
+
+ private String code;
+
+ public MyException() {
+ }
+
+ public MyException(String code, String message) {
+ super(message);
+ this.code = code;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/AnimalEnum.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/AnimalEnum.java
new file mode 100644
index 0000000..61ca153
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/AnimalEnum.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model;
+
+/**
+ * @author ding.lid
+ *
+ */
+public enum AnimalEnum {
+ dog, cat, rat, cow, bull, horse;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizException.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizException.java
new file mode 100644
index 0000000..f2bf7f8
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model;
+
+public class BizException extends RuntimeException{
+
+ private static final long serialVersionUID = 1L;
+
+ public BizException(String message){
+ super(message);
+ }
+
+ public BizException() {
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizExceptionNoDefaultConstructor.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizExceptionNoDefaultConstructor.java
new file mode 100644
index 0000000..92ae730
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/BizExceptionNoDefaultConstructor.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model;
+
+public class BizExceptionNoDefaultConstructor extends RuntimeException{
+
+ private static final long serialVersionUID = 1L;
+
+ public BizExceptionNoDefaultConstructor(String message){
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/Person.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/Person.java
new file mode 100644
index 0000000..85ae64d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/Person.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model;
+
+import java.util.Arrays;
+
+/**
+ * @author ding.lid
+ */
+public class Person {
+ private String name = "name1";
+
+ byte oneByte = 123;
+
+ private int age = 11;
+
+ private String[] value = {"value1", "value2"};
+
+ public String getName() {
+ return name;
+ }
+
+ public byte getOneByte() {
+ return oneByte;
+ }
+
+ public void setOneByte(byte b) {
+ this.oneByte = b;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String[] getValue() {
+ return value;
+ }
+
+ public void setValue(String[] value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value));
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + age;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + Arrays.hashCode(value);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Person other = (Person) obj;
+ if (age != other.age)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (!Arrays.equals(value, other.value))
+ return false;
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/SerializablePerson.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/SerializablePerson.java
new file mode 100644
index 0000000..a03b901
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/SerializablePerson.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * @author ding.lid
+ */
+public class SerializablePerson implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String name = "name1";
+
+ byte oneByte = 123;
+
+ private int age = 11;
+
+ private String[] value = {"value1", "value2"};
+
+ public String getName() {
+ return name;
+ }
+
+ public byte getOneByte() {
+ return oneByte;
+ }
+
+ public void setOneByte(byte b) {
+ this.oneByte = b;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String[] getValue() {
+ return value;
+ }
+
+ public void setValue(String[] value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value));
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + age;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + Arrays.hashCode(value);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SerializablePerson other = (SerializablePerson) obj;
+ if (age != other.age)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (!Arrays.equals(value, other.value))
+ return false;
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Image.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Image.java
new file mode 100644
index 0000000..478eb90
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Image.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model.media;
+
+
+public class Image implements java.io.Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ public enum Size {
+ SMALL, LARGE
+ }
+
+ public String uri;
+
+ public String title; // Can be null
+ public int width;
+ public int height;
+ public Size size;
+
+ public Image() {}
+
+ public Image (String uri, String title, int width, int height, Size size) {
+ this.height = height;
+ this.title = title;
+ this.uri = uri;
+ this.width = width;
+ this.size = size;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Image image = (Image) o;
+
+ if (height != image.height) return false;
+ if (width != image.width) return false;
+ if (size != image.size) return false;
+ if (title != null ? !title.equals(image.title) : image.title != null) return false;
+ if (uri != null ? !uri.equals(image.uri) : image.uri != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = uri != null ? uri.hashCode() : 0;
+ result = 31 * result + (title != null ? title.hashCode() : 0);
+ result = 31 * result + width;
+ result = 31 * result + height;
+ result = 31 * result + (size != null ? size.hashCode() : 0);
+ return result;
+ }
+
+ public String toString () {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[Image ");
+ sb.append("uri=").append(uri);
+ sb.append(", title=").append(title);
+ sb.append(", width=").append(width);
+ sb.append(", height=").append(height);
+ sb.append(", size=").append(size);
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public void setSize(Size size) {
+ this.size = size;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public Size getSize() {
+ return size;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Media.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Media.java
new file mode 100644
index 0000000..a3683bb
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/Media.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model.media;
+
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class Media implements java.io.Serializable {
+ public enum Player {
+ JAVA, FLASH
+ }
+
+ public String uri;
+ public String title; // Can be unset.
+ public int width;
+ public int height;
+ public String format;
+ public long duration;
+ public long size;
+ public int bitrate; // Can be unset.
+ public boolean hasBitrate;
+ public List<String> persons;
+
+ public Player player;
+
+ public String copyright; // Can be unset.
+
+ public Media() {}
+
+ public Media(String uri, String title, int width, int height, String format, long duration, long size, int bitrate, boolean hasBitrate, List<String> persons, Player player, String copyright)
+ {
+ this.uri = uri;
+ this.title = title;
+ this.width = width;
+ this.height = height;
+ this.format = format;
+ this.duration = duration;
+ this.size = size;
+ this.bitrate = bitrate;
+ this.hasBitrate = hasBitrate;
+ this.persons = persons;
+ this.player = player;
+ this.copyright = copyright;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Media media = (Media) o;
+
+ if (bitrate != media.bitrate) return false;
+ if (duration != media.duration) return false;
+ if (hasBitrate != media.hasBitrate) return false;
+ if (height != media.height) return false;
+ if (size != media.size) return false;
+ if (width != media.width) return false;
+ if (copyright != null ? !copyright.equals(media.copyright) : media.copyright != null) return false;
+ if (format != null ? !format.equals(media.format) : media.format != null) return false;
+ if (persons != null ? !persons.equals(media.persons) : media.persons != null) return false;
+ if (player != media.player) return false;
+ if (title != null ? !title.equals(media.title) : media.title != null) return false;
+ if (uri != null ? !uri.equals(media.uri) : media.uri != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = uri != null ? uri.hashCode() : 0;
+ result = 31 * result + (title != null ? title.hashCode() : 0);
+ result = 31 * result + width;
+ result = 31 * result + height;
+ result = 31 * result + (format != null ? format.hashCode() : 0);
+ result = 31 * result + (int) (duration ^ (duration >>> 32));
+ result = 31 * result + (int) (size ^ (size >>> 32));
+ result = 31 * result + bitrate;
+ result = 31 * result + (hasBitrate ? 1 : 0);
+ result = 31 * result + (persons != null ? persons.hashCode() : 0);
+ result = 31 * result + (player != null ? player.hashCode() : 0);
+ result = 31 * result + (copyright != null ? copyright.hashCode() : 0);
+ return result;
+ }
+
+ public String toString () {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[Media ");
+ sb.append("uri=").append(uri);
+ sb.append(", title=").append(title);
+ sb.append(", width=").append(width);
+ sb.append(", height=").append(height);
+ sb.append(", format=").append(format);
+ sb.append(", duration=").append(duration);
+ sb.append(", size=").append(size);
+ sb.append(", hasBitrate=").append(hasBitrate);
+ sb.append(", bitrate=").append(String.valueOf(bitrate));
+ sb.append(", persons=").append(persons);
+ sb.append(", player=").append(player);
+ sb.append(", copyright=").append(copyright);
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+ public void setDuration(long duration) {
+ this.duration = duration;
+ }
+
+ public void setSize(long size) {
+ this.size = size;
+ }
+
+ public void setBitrate(int bitrate) {
+ this.bitrate = bitrate;
+ this.hasBitrate = true;
+ }
+
+ public void setPersons(List<String> persons) {
+ this.persons = persons;
+ }
+
+ public void setPlayer(Player player) {
+ this.player = player;
+ }
+
+ public void setCopyright(String copyright) {
+ this.copyright = copyright;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public String getFormat() {
+ return format;
+ }
+
+ public long getDuration() {
+ return duration;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public int getBitrate() {
+ return bitrate;
+ }
+
+ public List<String> getPersons() {
+ return persons;
+ }
+
+ public Player getPlayer() {
+ return player;
+ }
+
+ public String getCopyright() {
+ return copyright;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/MediaContent.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/MediaContent.java
new file mode 100644
index 0000000..3a8d9e6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/media/MediaContent.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model.media;
+
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class MediaContent implements java.io.Serializable
+{
+ public Media media;
+ public List<Image> images;
+
+ public MediaContent() {}
+
+ public MediaContent (Media media, List<Image> images) {
+ this.media = media;
+ this.images = images;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MediaContent that = (MediaContent) o;
+
+ if (images != null ? !images.equals(that.images) : that.images != null) return false;
+ if (media != null ? !media.equals(that.media) : that.media != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = media != null ? media.hashCode() : 0;
+ result = 31 * result + (images != null ? images.hashCode() : 0);
+ return result;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[MediaContent: ");
+ sb.append("media=").append(media);
+ sb.append(", images=").append(images);
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public void setMedia(Media media) {
+ this.media = media;
+ }
+
+ public void setImages(List<Image> images) {
+ this.images = images;
+ }
+
+ public Media getMedia() {
+ return media;
+ }
+
+ public List<Image> getImages() {
+ return images;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/BigPerson.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/BigPerson.java
new file mode 100644
index 0000000..5165232
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/BigPerson.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model.person;
+
+import java.io.Serializable;
+
+/**
+ * @author tony.chenl
+ */
+public class BigPerson implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ String personId;
+
+ String loginName;
+
+ PersonStatus status;
+
+ String email;
+
+ String penName;
+
+ PersonInfo infoProfile;
+
+ public BigPerson() {
+
+ }
+
+ public BigPerson(String id) {
+ this.personId = id;
+ }
+
+ public String getPersonId() {
+ return personId;
+ }
+
+ public void setPersonId(String personId) {
+ this.personId = personId;
+ }
+
+ public PersonInfo getInfoProfile() {
+ return infoProfile;
+ }
+
+ public void setInfoProfile(PersonInfo infoProfile) {
+ this.infoProfile = infoProfile;
+ }
+
+ public void setLoginName(String loginName) {
+ this.loginName = loginName;
+ }
+
+ public void setStatus(PersonStatus status) {
+ this.status = status;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public void setPenName(String penName) {
+ this.penName = penName;
+ }
+
+ public String getEmail() {
+ return this.email;
+ }
+
+ public String getLoginName() {
+ return this.loginName;
+ }
+
+ public PersonStatus getStatus() {
+ return this.status;
+ }
+
+ public String getPenName() {
+ return penName;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((email == null) ? 0 : email.hashCode());
+ result = prime * result + ((infoProfile == null) ? 0 : infoProfile.hashCode());
+ result = prime * result + ((loginName == null) ? 0 : loginName.hashCode());
+ result = prime * result + ((penName == null) ? 0 : penName.hashCode());
+ result = prime * result + ((personId == null) ? 0 : personId.hashCode());
+ result = prime * result + ((status == null) ? 0 : status.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BigPerson other = (BigPerson) obj;
+ if (email == null) {
+ if (other.email != null)
+ return false;
+ } else if (!email.equals(other.email))
+ return false;
+ if (infoProfile == null) {
+ if (other.infoProfile != null)
+ return false;
+ } else if (!infoProfile.equals(other.infoProfile))
+ return false;
+ if (loginName == null) {
+ if (other.loginName != null)
+ return false;
+ } else if (!loginName.equals(other.loginName))
+ return false;
+ if (penName == null) {
+ if (other.penName != null)
+ return false;
+ } else if (!penName.equals(other.penName))
+ return false;
+ if (personId == null) {
+ if (other.personId != null)
+ return false;
+ } else if (!personId.equals(other.personId))
+ return false;
+ if (status != other.status)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "BigPerson [personId=" + personId + ", loginName=" + loginName + ", status="
+ + status + ", email=" + email + ", penName=" + penName + ", infoProfile="
+ + infoProfile + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/FullAddress.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/FullAddress.java
new file mode 100644
index 0000000..13d0d53
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/FullAddress.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model.person;
+
+import java.io.Serializable;
+
+/**
+ * @author xk1430
+ */
+public class FullAddress implements Serializable {
+
+ private static final long serialVersionUID = 5163979984269419831L;
+
+ private String countryId;
+
+ private String countryName;
+
+ private String provinceName;
+
+ private String cityId;
+
+ private String cityName;
+
+ private String streetAddress;
+
+ private String zipCode;
+
+ public void setCountryId(String countryId) {
+ this.countryId = countryId;
+ }
+
+ public void setCountryName(String countryName) {
+ this.countryName = countryName;
+ }
+
+ public void setProvinceName(String provinceName) {
+ this.provinceName = provinceName;
+ }
+
+ public void setCityId(String cityId) {
+ this.cityId = cityId;
+ }
+
+ public void setCityName(String cityName) {
+ this.cityName = cityName;
+ }
+
+ public void setStreetAddress(String streetAddress) {
+ this.streetAddress = streetAddress;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+
+ public String getCountryId() {
+ return countryId;
+ }
+
+ public String getCountryName() {
+ return countryName;
+ }
+
+ public String getProvinceName() {
+ return provinceName;
+ }
+
+ public String getCityId() {
+ return cityId;
+ }
+
+ public String getCityName() {
+ return cityName;
+ }
+
+ public String getStreetAddress() {
+ return streetAddress;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ public FullAddress() {
+ }
+
+ public FullAddress(String countryId, String provinceName, String cityId, String streetAddress,
+ String zipCode) {
+ this.countryId = countryId;
+ this.countryName = countryId;
+ this.provinceName = provinceName;
+ this.cityId = cityId;
+ this.cityName = cityId;
+ this.streetAddress = streetAddress;
+ this.zipCode = zipCode;
+ }
+
+ public FullAddress(String countryId, String countryName, String provinceName, String cityId,
+ String cityName, String streetAddress, String zipCode) {
+ this.countryId = countryId;
+ this.countryName = countryName;
+ this.provinceName = provinceName;
+ this.cityId = cityId;
+ this.cityName = cityName;
+ this.streetAddress = streetAddress;
+ this.zipCode = zipCode;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((cityId == null) ? 0 : cityId.hashCode());
+ result = prime * result + ((cityName == null) ? 0 : cityName.hashCode());
+ result = prime * result + ((countryId == null) ? 0 : countryId.hashCode());
+ result = prime * result + ((countryName == null) ? 0 : countryName.hashCode());
+ result = prime * result + ((provinceName == null) ? 0 : provinceName.hashCode());
+ result = prime * result + ((streetAddress == null) ? 0 : streetAddress.hashCode());
+ result = prime * result + ((zipCode == null) ? 0 : zipCode.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ FullAddress other = (FullAddress) obj;
+ if (cityId == null) {
+ if (other.cityId != null)
+ return false;
+ } else if (!cityId.equals(other.cityId))
+ return false;
+ if (cityName == null) {
+ if (other.cityName != null)
+ return false;
+ } else if (!cityName.equals(other.cityName))
+ return false;
+ if (countryId == null) {
+ if (other.countryId != null)
+ return false;
+ } else if (!countryId.equals(other.countryId))
+ return false;
+ if (countryName == null) {
+ if (other.countryName != null)
+ return false;
+ } else if (!countryName.equals(other.countryName))
+ return false;
+ if (provinceName == null) {
+ if (other.provinceName != null)
+ return false;
+ } else if (!provinceName.equals(other.provinceName))
+ return false;
+ if (streetAddress == null) {
+ if (other.streetAddress != null)
+ return false;
+ } else if (!streetAddress.equals(other.streetAddress))
+ return false;
+ if (zipCode == null) {
+ if (other.zipCode != null)
+ return false;
+ } else if (!zipCode.equals(other.zipCode))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (countryName != null && countryName.length() > 0) {
+ sb.append(countryName);
+ }
+ if (provinceName != null && provinceName.length() > 0) {
+ sb.append(" ");
+ sb.append(provinceName);
+ }
+ if (cityName != null && cityName.length() > 0) {
+ sb.append(" ");
+ sb.append(cityName);
+ }
+ if (streetAddress != null && streetAddress.length() > 0) {
+ sb.append(" ");
+ sb.append(streetAddress);
+ }
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonInfo.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonInfo.java
new file mode 100644
index 0000000..2c48441
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonInfo.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model.person;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author tony.chenl
+ */
+public class PersonInfo implements Serializable {
+ private static final long serialVersionUID = 7443011149612231882L;
+
+ List<Phone> phones;
+
+ Phone fax;
+
+ FullAddress fullAddress;
+
+ String mobileNo;
+
+ String name;
+
+ boolean male;
+
+ boolean female;
+
+ String department;
+
+ String jobTitle;
+
+ String homepageUrl;
+
+ public List<Phone> getPhones() {
+ return phones;
+ }
+
+ public void setPhones(List<Phone> phones) {
+ this.phones = phones;
+ }
+
+ public boolean isMale() {
+ return male;
+ }
+
+ public void setMale(boolean male) {
+ this.male = male;
+ }
+
+ public boolean isFemale() {
+ return female;
+ }
+
+ public void setFemale(boolean female) {
+ this.female = female;
+ }
+
+ public void setFax(Phone fax) {
+ this.fax = fax;
+ }
+
+ public void setFullAddress(FullAddress fullAddress) {
+ this.fullAddress = fullAddress;
+ }
+
+ public void setMobileNo(String mobileNo) {
+ this.mobileNo = mobileNo;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setDepartment(String department) {
+ this.department = department;
+ }
+
+ public void setJobTitle(String jobTitle) {
+ this.jobTitle = jobTitle;
+ }
+
+ public void setHomepageUrl(String homepageUrl) {
+ this.homepageUrl = homepageUrl;
+ }
+
+ public String getDepartment() {
+ return department;
+ }
+
+ public Phone getFax() {
+ return fax;
+ }
+
+ public FullAddress getFullAddress() {
+ return fullAddress;
+ }
+
+ public String getHomepageUrl() {
+ return homepageUrl;
+ }
+
+ public String getJobTitle() {
+ return jobTitle;
+ }
+
+ public String getMobileNo() {
+ return mobileNo;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((department == null) ? 0 : department.hashCode());
+ result = prime * result + ((fax == null) ? 0 : fax.hashCode());
+ result = prime * result + (female ? 1231 : 1237);
+ result = prime * result + ((fullAddress == null) ? 0 : fullAddress.hashCode());
+ result = prime * result + ((homepageUrl == null) ? 0 : homepageUrl.hashCode());
+ result = prime * result + ((jobTitle == null) ? 0 : jobTitle.hashCode());
+ result = prime * result + (male ? 1231 : 1237);
+ result = prime * result + ((mobileNo == null) ? 0 : mobileNo.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((phones == null) ? 0 : phones.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ PersonInfo other = (PersonInfo) obj;
+ if (department == null) {
+ if (other.department != null)
+ return false;
+ } else if (!department.equals(other.department))
+ return false;
+ if (fax == null) {
+ if (other.fax != null)
+ return false;
+ } else if (!fax.equals(other.fax))
+ return false;
+ if (female != other.female)
+ return false;
+ if (fullAddress == null) {
+ if (other.fullAddress != null)
+ return false;
+ } else if (!fullAddress.equals(other.fullAddress))
+ return false;
+ if (homepageUrl == null) {
+ if (other.homepageUrl != null)
+ return false;
+ } else if (!homepageUrl.equals(other.homepageUrl))
+ return false;
+ if (jobTitle == null) {
+ if (other.jobTitle != null)
+ return false;
+ } else if (!jobTitle.equals(other.jobTitle))
+ return false;
+ if (male != other.male)
+ return false;
+ if (mobileNo == null) {
+ if (other.mobileNo != null)
+ return false;
+ } else if (!mobileNo.equals(other.mobileNo))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (phones == null) {
+ if (other.phones != null)
+ return false;
+ } else if (!phones.equals(other.phones))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "PersonInfo [phones=" + phones + ", fax=" + fax + ", fullAddress=" + fullAddress
+ + ", mobileNo=" + mobileNo + ", name=" + name + ", male=" + male + ", female="
+ + female + ", department=" + department + ", jobTitle=" + jobTitle
+ + ", homepageUrl=" + homepageUrl + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonStatus.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonStatus.java
new file mode 100644
index 0000000..75a4d58
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/PersonStatus.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model.person;
+
+/**
+ * @author tony.chenl
+ */
+public enum PersonStatus {
+ ENABLED,
+ DISABLED
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/Phone.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/Phone.java
new file mode 100644
index 0000000..8ee36d5
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/model/person/Phone.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.model.person;
+
+import java.io.Serializable;
+
+/**
+ * 电话号码
+ *
+ * @author xk1430
+ */
+public class Phone implements Serializable {
+
+ private static final long serialVersionUID = 4399060521859707703L;
+
+ private String country;
+
+ private String area;
+
+ private String number;
+
+ private String extensionNumber;
+
+ public Phone() {
+ }
+
+ public Phone(String country, String area, String number, String extensionNumber) {
+ this.country = country;
+ this.area = area;
+ this.number = number;
+ this.extensionNumber = extensionNumber;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public void setArea(String area) {
+ this.area = area;
+ }
+
+ public void setNumber(String number) {
+ this.number = number;
+ }
+
+ public void setExtensionNumber(String extensionNumber) {
+ this.extensionNumber = extensionNumber;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public String getArea() {
+ return area;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public String getExtensionNumber() {
+ return extensionNumber;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((area == null) ? 0 : area.hashCode());
+ result = prime * result + ((country == null) ? 0 : country.hashCode());
+ result = prime * result + ((extensionNumber == null) ? 0 : extensionNumber.hashCode());
+ result = prime * result + ((number == null) ? 0 : number.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Phone other = (Phone) obj;
+ if (area == null) {
+ if (other.area != null)
+ return false;
+ } else if (!area.equals(other.area))
+ return false;
+ if (country == null) {
+ if (other.country != null)
+ return false;
+ } else if (!country.equals(other.country))
+ return false;
+ if (extensionNumber == null) {
+ if (other.extensionNumber != null)
+ return false;
+ } else if (!extensionNumber.equals(other.extensionNumber))
+ return false;
+ if (number == null) {
+ if (other.number != null)
+ return false;
+ } else if (!number.equals(other.number))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (country != null && country.length() > 0) {
+ sb.append(country);
+ sb.append("-");
+ }
+ if (area != null && area.length() > 0) {
+ sb.append(area);
+ sb.append("-");
+ }
+ if (number != null && number.length() > 0) {
+ sb.append(number);
+ }
+ if (extensionNumber != null && extensionNumber.length() > 0) {
+ sb.append("-");
+ sb.append(extensionNumber);
+ }
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/SerializationCompareTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/SerializationCompareTest.java
new file mode 100644
index 0000000..a0bb1ea
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/SerializationCompareTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.junit.Test;
+
+import com.alibaba.com.caucho.hessian.io.Hessian2Input;
+import com.alibaba.com.caucho.hessian.io.Hessian2Output;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.serialize.support.dubbo.Builder;
+import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectInputStream;
+import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectOutputStream;
+
+/**
+ * @author qian.lei
+ * @author ding.lid
+ */
+public class SerializationCompareTest
+{
+ @Test
+ public void test_CompareSerializeLength() throws Exception
+ {
+ long[] data = new long[]{ -1l, 2l, 3l, 4l, 5l };
+ ByteArrayOutputStream os;
+
+ os = new ByteArrayOutputStream();
+ ObjectOutputStream jos = new ObjectOutputStream(os);
+ jos.writeObject(data);
+ System.out.println("java:"+Bytes.bytes2hex(os.toByteArray())+":"+os.size());
+
+ os = new ByteArrayOutputStream();
+ CompactedObjectOutputStream oos = new CompactedObjectOutputStream(os);
+ oos.writeObject(data);
+ System.out.println("compacted java:"+Bytes.bytes2hex(os.toByteArray())+":"+os.size());
+
+ os = new ByteArrayOutputStream();
+ Hessian2Output h2o = new Hessian2Output(os);
+ h2o.writeObject(data);
+ h2o.flushBuffer();
+ System.out.println("hessian:"+Bytes.bytes2hex(os.toByteArray())+":"+os.size());
+
+ os = new ByteArrayOutputStream();
+ Builder<long[]> lb = Builder.register(long[].class);
+ lb.writeTo(data, os);
+ System.out.println("DataOutput:"+Bytes.bytes2hex(os.toByteArray())+":"+os.size());
+ }
+
+ @Test
+ public void testBuilderPerm() throws Exception
+ {
+ Builder<Bean> bb = Builder.register(Bean.class);
+ Bean bean = new Bean();
+ int len = 0;
+ long now = System.currentTimeMillis();
+ for(int i=0;i<500;i++)
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ bb.writeTo(bean, os);
+ os.close();
+ if( i == 0 )
+ len = os.toByteArray().length;
+
+ ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
+ Bean b = bb.parseFrom(is);
+ assertEquals(b.getClass(), Bean.class);
+ }
+ System.out.println("Builder write and parse 500 times in " + (System.currentTimeMillis()-now)+"ms, size " + len);
+ }
+
+ @Test
+ public void testH2oPerm() throws Exception
+ {
+ Bean bean = new Bean();
+ int len = 0;
+ long now = System.currentTimeMillis();
+ for(int i=0;i<500;i++)
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ Hessian2Output out = new Hessian2Output(os);
+ out.writeObject(bean);
+ out.flushBuffer();
+ os.close();
+ if( i == 0 )
+ len = os.toByteArray().length;
+ ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
+ Hessian2Input in = new Hessian2Input(is);
+ assertEquals(in.readObject().getClass(), Bean.class);
+ }
+ System.out.println("Hessian2 write and parse 500 times in " + (System.currentTimeMillis()-now)+"ms, size " + len);
+ }
+
+ @Test
+ public void testJavaOutputPerm() throws Exception
+ {
+ Bean bean = new Bean();
+ int len = 0;
+ long now = System.currentTimeMillis();
+ for(int i=0;i<500;i++)
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(os);
+ out.writeObject(bean);
+ os.close();
+ if( i == 0 )
+ len = os.toByteArray().length;
+ ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
+ ObjectInputStream in = new ObjectInputStream(is);
+ assertEquals(in.readObject().getClass(), Bean.class);
+ }
+ System.out.println("java write and parse 500 times in " + (System.currentTimeMillis()-now)+"ms, size " + len);
+ }
+
+ @Test
+ public void testCompactedJavaOutputPerm() throws Exception
+ {
+ Bean bean = new Bean();
+ int len = 0;
+ long now = System.currentTimeMillis();
+ for(int i=0;i<500;i++)
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ CompactedObjectOutputStream out = new CompactedObjectOutputStream(os);
+ out.writeObject(bean);
+ os.close();
+ if( i == 0 )
+ len = os.toByteArray().length;
+ ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
+ CompactedObjectInputStream in = new CompactedObjectInputStream(is);
+ assertEquals(in.readObject().getClass(), Bean.class);
+ }
+ System.out.println("compacted java write and parse 500 times in " + (System.currentTimeMillis()-now)+"ms, size " + len);
+ }
+
+ public static enum EnumTest { READ, WRITE, CREATE, UNREGISTER };
+
+ static class MyList<T> extends ArrayList<T>
+ {
+ private static final long serialVersionUID = 1L;
+
+ private int code = 12345;
+ private String id = "feedback";
+ }
+
+ static class MyMap<K, V> extends HashMap<K, V>
+ {
+ private static final long serialVersionUID = 1L;
+
+ private int code = 12345;
+ private String id = "feedback";
+ }
+
+ public static class Bean implements Serializable
+ {
+ private static final long serialVersionUID = 7737610585231102146L;
+
+ public EnumTest ve = EnumTest.CREATE;
+
+ public int vi = 0;
+ public long vl = 100l;
+
+ boolean b = true;
+ boolean[] bs = {false, true};
+
+ String s1 = "1234567890";
+ String s2 = "1234567890一二三四五六七八九零";
+
+ int i = 123123, ni = -12344, is[] = {1,2,3,4,-1,-2,-3,-4};
+ short s = 12, ns = -76;
+ double d = 12.345, nd = -12.345;
+ long l = 1281447759383l, nl = -13445l;
+ private ArrayList<Object> mylist = new ArrayList<Object>();
+ {
+ mylist.add(1);
+ mylist.add("qianlei");
+ mylist.add("qianlei");
+ mylist.add("qianlei");
+ mylist.add("qianlei");
+ }
+ private HashMap<Object, Object> mymap = new HashMap<Object, Object>();
+ {
+ mymap.put(1,2);
+ mymap.put(2,"1234");
+ mymap.put("2345",12938.122);
+ mymap.put("2345",-1);
+ mymap.put("2345",-1.20);
+ }
+
+ public ArrayList<Object> getMylist()
+ {
+ return mylist;
+ }
+
+ public void setMylist(ArrayList<Object> list)
+ {
+ mylist = list;
+ }
+
+ public HashMap<Object, Object> getMymap()
+ {
+ return mymap;
+ }
+
+ public void setMymap(HashMap<Object, Object> map)
+ {
+ mymap = map;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/BuilderTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/BuilderTest.java
new file mode 100644
index 0000000..a9b688d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/BuilderTest.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.dubbo;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import java.io.Serializable;
+import java.lang.reflect.Modifier;
+import java.sql.Time;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.serialize.support.dubbo.Builder;
+
+public class BuilderTest
+{
+ @Test
+ public void testPrimaryTypeBuilder() throws Exception
+ {
+ System.out.println((new byte[2]).hashCode());
+ Builder<String> builder = Builder.register(String.class);
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ String v = "123";
+ builder.writeTo(v, os);
+ byte[] b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+ v = builder.parseFrom(b);
+ builder.writeTo(v, os);
+ b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+ }
+
+ @Test
+ public void testEnumBuilder() throws Exception
+ {
+ Builder<Type> builder = Builder.register(Type.class);
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ Type v = Type.High;
+ builder.writeTo(v, os);
+ byte[] b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+ v = builder.parseFrom(b);
+ }
+
+ @Test
+ public void testThrowableBuilder() throws Exception
+ {
+ Builder<Throwable> builder = Builder.register(Throwable.class);
+ Throwable th = new Throwable();
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ builder.writeTo(th, os);
+ byte[] b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+
+ th = builder.parseFrom(b);
+ }
+
+ @Test
+ public void testArrayClassBuilder() throws Exception
+ {
+ UnsafeByteArrayOutputStream os;
+
+ byte[] b;
+
+ Builder<Object[]> osb = Builder.register(Object[].class);
+ os = new UnsafeByteArrayOutputStream();
+ osb.writeTo(new Object[]{ new String[0] }, os);
+ b = os.toByteArray();
+
+ Builder<long[]> lsb = Builder.register(long[].class);
+ os = new UnsafeByteArrayOutputStream();
+ lsb.writeTo(new long[]{ 1,121232,-3,4,-5,61321432413l }, os);
+ lsb.writeTo(new long[]{ 1,121232,-3,4,-5,61321432413l }, os);
+ lsb.writeTo(new long[]{ 1,2,3,12131314,123132313135l,-6 }, os);
+ b = os.toByteArray();
+ long[] ls = lsb.parseFrom(b);
+ assertEquals(ls.length, 6);
+
+ Builder<byte[]> bsb = Builder.register(byte[].class);
+ os = new UnsafeByteArrayOutputStream();
+ bsb.writeTo("i am a string.".getBytes(), os);
+ b = os.toByteArray();
+
+ Builder<int[][]> iisb = Builder.register(int[][].class);
+ os = new UnsafeByteArrayOutputStream();
+ iisb.writeTo(new int[][]{ {1,2,3,4}, {5,6,7,8}, {9,10}, {122,123,444} }, os);
+ b = os.toByteArray();
+ int[][] iis = iisb.parseFrom(b);
+ assertEquals(iis.length, 4);
+
+ Builder<int[][][]> iiisb = Builder.register(int[][][].class);
+ os = new UnsafeByteArrayOutputStream();
+ iiisb.writeTo(new int[][][]{
+ {{1,2,3,4}},
+ {{5,6,7,8}},
+ {{122,123,444}}
+ }, os);
+ b = os.toByteArray();
+ int[][][] iii = iiisb.parseFrom(b);
+ assertEquals(iii.length, 3);
+ }
+
+ @Test
+ public void testObjectBuilder() throws Exception
+ {
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ Builder<Bean> BeanBuilder = Builder.register(Bean.class);
+
+ Bean bean = new Bean();
+ bean.name = "ql";
+ bean.type = Type.High;
+ bean.types = new Type[]{ Type.High, Type.High };
+ BeanBuilder.writeTo(bean, os);
+
+ byte[] b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+
+ bean = BeanBuilder.parseFrom(b);
+ assertNull(bean.time);
+ assertEquals(bean.i, 123123);
+ assertEquals(bean.ni, -12344);
+ assertEquals(bean.d, 12.345);
+ assertEquals(bean.nd, -12.345);
+ assertEquals(bean.l, 1281447759383l);
+ assertEquals(bean.nl, -13445l);
+ assertEquals(bean.vl, 100l);
+ assertEquals(bean.type, Type.High);
+ assertEquals(bean.types.length, 2);
+ assertEquals(bean.types[0], Type.High);
+ assertEquals(bean.types[1], Type.High);
+ assertEquals(bean.list.size(), 3);
+ assertEquals(bean.list.get(0), 1);
+ assertEquals(bean.list.get(1), 2);
+ assertEquals(bean.list.get(2), 1308147);
+ }
+
+ @Test
+ public void testInterfaceBuilder() throws Exception
+ {
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ Builder<TestDO> builder = Builder.register(TestDO.class);
+ TestDO d = new TestDOImpl();
+ builder.writeTo(d, os);
+
+ byte[] b = os.toByteArray();
+
+ d = builder.parseFrom(b);
+ assertTrue(TestDO.class.isAssignableFrom(d.getClass()));
+ assertEquals("name", d.getName());
+ assertEquals(28, d.getArg());
+ assertEquals(Type.High, d.getType());
+ }
+
+ @Test
+ public void testGenericBuilder() throws Exception
+ {
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ Builder<Object> ob = Builder.register(Object.class);
+
+ Object o = new Object();
+ ob.writeTo(o, os);
+ byte[] b = os.toByteArray();
+
+ os = new UnsafeByteArrayOutputStream();
+ Bean bean = new Bean();
+ bean.name = "ql";
+ bean.type = Type.High;
+ bean.types = new Type[]{ Type.High, Type.High };
+ ob.writeTo(bean, os);
+
+ b = os.toByteArray();
+ bean = (Bean)ob.parseFrom(b);
+ assertEquals(bean.i, 123123);
+ assertEquals(bean.ni, -12344);
+ assertEquals(bean.d, 12.345);
+ assertEquals(bean.nd, -12.345);
+ assertEquals(bean.l, 1281447759383l);
+ assertEquals(bean.nl, -13445l);
+ assertEquals(bean.vl, 100l);
+ assertEquals(bean.type, Type.High);
+ assertEquals(bean.types.length, 2);
+ assertEquals(bean.types[0], Type.High);
+ assertEquals(bean.types[1], Type.High);
+ assertEquals(bean.list.size(), 3);
+ assertEquals(bean.list.get(0), 1);
+ assertEquals(bean.list.get(1), 2);
+ assertEquals(bean.list.get(2), 1308147);
+ }
+
+ @Test
+ public void testObjectArrayBuilder() throws Exception
+ {
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ Builder<Object[]> builder = Builder.register(Object[].class);
+
+ Object[] obj = new Object[5];
+ obj[0] = "1234";
+ obj[1] = new Double(109.23);
+ obj[2] = "3455";
+ obj[3] = null;
+ obj[4] = Boolean.TRUE;
+
+ builder.writeTo(obj, os);
+ byte[] b = os.toByteArray();
+ System.out.println("Object array:"+b.length+":"+Bytes.bytes2hex(b));
+
+ Assert.assertArrayEquals(obj, builder.parseFrom(b));
+ }
+
+ // FIXME MyList的从ArrayList中继承来的属性size会在decode时设置好,再Add时就不对了!!
+ @Ignore
+ @Test
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void testBuilder_MyList() throws Exception
+ {
+ Builder<MyList> b1 = Builder.register(MyList.class);
+ MyList list = new MyList();
+ list.add(new boolean[]{ true,false });
+ list.add(new int[]{ 1,2,3,4,5 });
+ list.add("String");
+ list.add(4);
+ list.code = 4321;
+
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ b1.writeTo(list, os);
+ byte[] b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+ MyList result = b1.parseFrom(b);
+
+ assertEquals(4, result.size());
+ assertEquals(result.code, 4321);
+ assertEquals(result.id, "feedback");
+ }
+
+ @Test
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void testBuilder_MyMap() throws Exception
+ {
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ Builder<MyMap> b2 = Builder.register(MyMap.class);
+ MyMap map = new MyMap();
+ map.put("name", "qianlei");
+ map.put("displayName", "钱磊");
+ map.code = 4321;
+ b2.writeTo(map, os);
+ byte[] b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+
+ map = b2.parseFrom(b);
+
+ assertEquals(map.size(), 2);
+ assertEquals(map.code, 4321);
+ assertEquals(map.id, "feedback");
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testSerializableBean() throws Exception
+ {
+ System.out.println("testSerializableBean");
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+
+ SerializableBean sb = new SerializableBean();
+ Builder<SerializableBean> sbb = Builder.register(SerializableBean.class);
+ sbb.writeTo(sb, os);
+
+ byte[] b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+ assertEquals(sbb.parseFrom(os.toByteArray()), sb);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testOthers() throws Exception
+ {
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+
+ StringBuffer buf = new StringBuffer();
+ for(int i=0;i<1024*32+32;i++)
+ buf.append('A');
+ Builder<String> sb = Builder.register(String.class);
+ sb.writeTo(buf.toString(), os);
+ assertEquals(sb.parseFrom(os.toByteArray()), buf.toString());
+
+ os = new UnsafeByteArrayOutputStream();
+ Builder<HashMap> builder = Builder.register(HashMap.class);
+ Map services = new HashMap();
+ HashMap map = new HashMap();
+ services.put("test.service", "http://127.0.0.1:9010/test.service");
+ map.put("name", "qianlei");
+ map.put("password", "123455");
+ map.put("services", services);
+
+ builder.writeTo(map, os);
+ byte[] b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+ map = builder.parseFrom(b);
+ assertTrue(map.size() > 0);
+ assertEquals("http://127.0.0.1:9010/test.service", ((Map) map.get("services")).get("test.service"));
+
+ services = new ConcurrentHashMap();
+ services.put("test.service", "http://127.0.0.1:9010/test.service");
+ map.put("services", services);
+
+ os = new UnsafeByteArrayOutputStream();
+ builder.writeTo(map, os);
+ b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+ map = builder.parseFrom(b);
+ assertTrue(map.size() > 0);
+ assertEquals("http://127.0.0.1:9010/test.service", ((Map) map.get("services")).get("test.service"));
+
+ Node node1 = new Node();
+ Node node0 = new Node();
+ node0.value = "0";
+ node0.next = node1;
+ node1.value = "1";
+ node1.prev = node0;
+ // write.
+ Builder<Node> nodebuilder = Builder.register(Node.class);
+ os = new UnsafeByteArrayOutputStream();
+ nodebuilder.writeTo(node0, os);
+ b = os.toByteArray();
+ System.out.println("Node:"+b.length+":"+Bytes.bytes2hex(b));
+ // parse
+ node0 = nodebuilder.parseFrom(b);
+ assertEquals(node0, node0.prev);
+ assertEquals(node0, node0.next.prev);
+ assertEquals(node0.value, "0");
+ }
+ public static void main(String[] args) {
+ System.out.println(Modifier.isPublic(String.class.getModifiers()));
+ }
+ @Test
+ public void testWithFC() throws Exception
+ {
+ Builder<SimpleDO> builder = Builder.register(SimpleDO.class);
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+
+ SimpleDO sd = new SimpleDO();
+ sd.a = 1;
+ sd.b = 2;
+ sd.c = 3;
+ sd.str1 = "12345";
+ sd.str2 = "54321";
+ builder.writeTo(sd, os);
+ byte[] b = os.toByteArray();
+ System.out.println(b.length+":"+Bytes.bytes2hex(b));
+
+ sd = builder.parseFrom(b);
+ assertEquals(sd.a, 1);
+ assertEquals(sd.b, 2);
+ assertEquals(sd.c, 3);
+ assertEquals(sd.str1, "124");
+ System.out.println(sd.str2);
+ }
+
+ public enum Type
+ {
+ Lower, Normal, High;
+ }
+
+ static interface TestDO
+ {
+ String getName();
+ void setName(String name);
+ Type getType();
+ void setType(Type t);
+ int getArg();
+ void setArg(int arg);
+ }
+
+ static class TestDOImpl implements TestDO, Serializable
+ {
+ private static final long serialVersionUID = 1L;
+ public String getName()
+ {
+ return "name";
+ }
+ public void setName(String name){}
+ public Type getType()
+ {
+ return Type.High;
+ }
+ public void setType(Type t){}
+ public int getArg()
+ {
+ return 28;
+ }
+ public void setArg(int arg){}
+ }
+
+ static class Bean implements Serializable
+ {
+ private static final long serialVersionUID = 1L;
+ public int vi = 0;
+ public long vl = 100l;
+
+ boolean b = true;
+ boolean[] bs = {false, true};
+
+ String s1 = "1234567890";
+ String s2 = "1234567890一二三四五六七八九零";
+
+ private int i = 123123, ni = -12344, is[] = {1,2,3,4,-1,-2,-3,-4};
+ private short s = 12, ns = -76;
+ private double d = 12.345, nd = -12.345;
+ private long l = 1281447759383l, nl = -13445l;
+
+ private Boolean B = Boolean.FALSE;
+ private Integer I = -1234;
+ private Double D = new Double(1.23);
+ private String name = "qianlei";
+ private Type type = Type.Lower, type1 = Type.Normal;
+ private Type[] types = { Type.Lower, Type.Lower };
+
+ private Time time = null;
+
+ public Type getType()
+ {
+ return type;
+ }
+
+ public void setType(Type type)
+ {
+ this.type = type;
+ }
+
+ private ArrayList list = new ArrayList();
+ {
+ list.add(1);
+ list.add(2);
+ list.add(1308147);
+ }
+ }
+
+ static class MyList<T> extends ArrayList<T>
+ {
+ private int code = 12345;
+ private String id = "feedback";
+ }
+
+ static class MyMap<K, V> extends HashMap<K, V>
+ {
+ private int code = 12345;
+ private String id = "feedback";
+ }
+
+ static class Node implements Serializable
+ {
+ private static final long serialVersionUID = 1L;
+ Node prev = this;
+ Node next = this;
+ String value = "value";
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ Node other = (Node) obj;
+ if (value == null) {
+ if (other.value != null) return false;
+ } else if (!value.equals(other.value)) return false;
+ return true;
+ }
+ }
+
+ static class SerializableBean implements Serializable
+ {
+ private static final long serialVersionUID = -8949681707161463700L;
+
+ public int a = 0;
+ public long b = 100l;
+ boolean c = true;
+ String s1 = "1234567890";
+ String s2 = "1234567890一二三四五六七八九零";
+
+ public int hashCode()
+ {
+ return s1.hashCode() ^ s2.hashCode();
+ }
+
+ public boolean equals(Object obj)
+ {
+ if( obj == null ) return false;
+ if( obj == this ) return true;
+ if( obj instanceof SerializableBean )
+ {
+ SerializableBean sb = (SerializableBean)obj;
+ return this.a == sb.a && this.b == sb.b && this.c == sb.c && this.s1.equals(sb.s1) && this.s2.equals(sb.s2);
+ }
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/DataInputOutputTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/DataInputOutputTest.java
new file mode 100644
index 0000000..cfaf8a0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/DataInputOutputTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.dubbo;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.serialize.DataInput;
+import com.alibaba.dubbo.common.serialize.DataOutput;
+import com.alibaba.dubbo.common.serialize.support.dubbo.GenericDataInput;
+import com.alibaba.dubbo.common.serialize.support.dubbo.GenericDataOutput;
+
+public class DataInputOutputTest extends TestCase
+{
+ private static final String SMALL_STRING = DataInputOutputTest.class.getName(), BIG_STREAM = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+
+ private static final byte[] SMALL_BYTES = SMALL_STRING.getBytes(), BIG_BYTES = BIG_STREAM.getBytes();
+
+ public void testMain() throws Exception
+ {
+ // write.
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream();
+ DataOutput cos = new GenericDataOutput(os);
+ writeTest(cos);
+
+ // read.
+ byte[] b = os.toByteArray();
+ DataInput cis = new GenericDataInput(new UnsafeByteArrayInputStream(b));
+ readTest(cis);
+ }
+
+ private void writeTest(DataOutput out) throws IOException
+ {
+ out.writeShort((short)'a');
+ out.writeShort((short)-1);
+ out.writeShort((short)1234);
+ out.writeInt(0x22);
+ out.writeInt(-0x22);
+ out.writeInt(0x2222);
+ out.writeInt(-0x2222);
+ out.writeInt(0x222222);
+ out.writeInt(-0x222222);
+ out.writeInt(0x22222222);
+ out.writeInt(-0x22222222);
+ out.writeLong(0x22);
+ out.writeLong(-0x22);
+ out.writeLong(0x2222);
+ out.writeLong(-0x2222);
+ out.writeLong(0x222222);
+ out.writeLong(-0x222222);
+ out.writeLong(0x22222222);
+ out.writeLong(-0x22222222);
+ out.writeLong(0x2222222222l);
+ out.writeLong(-0x2222222222l);
+ out.writeLong(0x222222222222l);
+ out.writeLong(-0x222222222222l);
+ out.writeLong(0x22222222222222l);
+ out.writeLong(-0x22222222222222l);
+ out.writeLong(0x2222222222222222l);
+ out.writeLong(-0x2222222222222222l);
+ out.writeDouble(1212.454);
+ out.writeBytes(SMALL_BYTES);
+ out.writeUTF(SMALL_STRING);
+ out.writeBytes(BIG_BYTES);
+ out.flushBuffer();
+ }
+
+ private void readTest(DataInput in) throws IOException
+ {
+ assertEquals(in.readShort(), 'a');
+ assertEquals(in.readShort(), -1);
+ assertEquals(in.readShort(), 1234);
+ assertEquals(in.readInt(), 0x22);
+ assertEquals(in.readInt(), -0x22);
+ assertEquals(in.readInt(), 0x2222);
+ assertEquals(in.readInt(), -0x2222);
+ assertEquals(in.readInt(), 0x222222);
+ assertEquals(in.readInt(), -0x222222);
+ assertEquals(in.readInt(), 0x22222222);
+ assertEquals(in.readInt(), -0x22222222);
+ assertEquals(in.readLong(), 0x22);
+ assertEquals(in.readLong(), -0x22);
+ assertEquals(in.readLong(), 0x2222);
+ assertEquals(in.readLong(), -0x2222);
+ assertEquals(in.readLong(), 0x222222);
+ assertEquals(in.readLong(), -0x222222);
+ assertEquals(in.readLong(), 0x22222222);
+ assertEquals(in.readLong(), -0x22222222);
+ assertEquals(in.readLong(), 0x2222222222l);
+ assertEquals(in.readLong(), -0x2222222222l);
+ assertEquals(in.readLong(), 0x222222222222l);
+ assertEquals(in.readLong(), -0x222222222222l);
+ assertEquals(in.readLong(), 0x22222222222222l);
+ assertEquals(in.readLong(), -0x22222222222222l);
+ assertEquals(in.readLong(), 0x2222222222222222l);
+ assertEquals(in.readLong(), -0x2222222222222222l);
+ assertEquals(in.readDouble(), 1212.454);
+ assertSameArray(in.readBytes(), SMALL_BYTES);
+ assertEquals(in.readUTF(), SMALL_STRING);
+ assertSameArray(in.readBytes(), BIG_BYTES);
+ }
+
+ private static void assertSameArray(byte[] b1, byte[] b2)
+ {
+ assertEquals(b1.length, b2.length);
+ for(int i=0;i<b1.length;i++)
+ assertEquals(b1[i], b2[i]);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.java
new file mode 100644
index 0000000..da67d72
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.dubbo;
+
+import java.io.Serializable;
+
+public class SimpleDO implements Serializable
+{
+
+ private static final long serialVersionUID = 1L;
+
+ public int a, b, c;
+
+ float d = 1.2f, e = 12.56f;
+
+ String str1 = "124", str2;
+
+ public int str3;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionFailTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionFailTest.java
new file mode 100644
index 0000000..f90368c
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionFailTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.io.NotSerializableException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.model.Person;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * @author ding.lid
+ */
+public abstract class AbstractSerializationPersionFailTest extends AbstractSerializationTest {
+ @Test
+ public void test_Person() throws Exception {
+ try {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(new Person());
+ fail();
+ }
+ catch (NotSerializableException expected) {}
+ catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+ }
+ }
+
+ @Test
+ public void test_PersonList() throws Exception {
+ List<Person> args = new ArrayList<Person>();
+ args.add(new Person());
+ try {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(args);
+ fail();
+ }
+ catch (NotSerializableException expected) {}
+ catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+ }
+ }
+
+ @Test
+ public void test_PersonSet() throws Exception {
+ Set<Person> args = new HashSet<Person>();
+ args.add(new Person());
+ try {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(args);
+ fail();
+ }
+ catch (NotSerializableException expected) {}
+ catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+ }
+ }
+
+ @Test
+ public void test_IntPersonMap() throws Exception {
+ Map<Integer, Person> args = new HashMap<Integer, Person>();
+ args.put(1, new Person());
+ try {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(args);
+ fail();
+ }
+ catch (NotSerializableException expected) {}
+ catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+ }
+ }
+
+ @Test
+ public void test_StringPersonMap() throws Exception {
+ Map<String, Person> args = new HashMap<String, Person>();
+ args.put("1", new Person());
+ try {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(args);
+ fail();
+ }
+ catch (NotSerializableException expected) {}
+ catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+ }
+ }
+
+ @Test
+ public void test_StringPersonListMap() throws Exception {
+ Map<String, List<Person>> args = new HashMap<String, List<Person>>();
+
+ List<Person> sublist = new ArrayList<Person>();
+ sublist.add(new Person());
+ args.put("1", sublist);
+ try {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(args);
+ fail();
+ }
+ catch (NotSerializableException expected) {}
+ catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+ }
+ }
+
+ @Test
+ public void test_PersonListList() throws Exception {
+ List<List<Person>> args = new ArrayList<List<Person>>();
+ List<Person> sublist = new ArrayList<Person>();
+ sublist.add(new Person());
+ args.add(sublist);
+ try {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(args);
+ fail();
+ }
+ catch (NotSerializableException expected) {}
+ catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Serialized class com.alibaba.dubbo.common.model.Person must implement java.io.Serializable"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionOkTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionOkTest.java
new file mode 100644
index 0000000..4176a80
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationPersionOkTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.model.Person;
+
+/**
+ * @author ding.lid
+ */
+public abstract class AbstractSerializationPersionOkTest extends AbstractSerializationTest {
+ @Test
+ public void test_Person() throws Exception {
+ assertObject(new Person());
+ }
+
+ @Test
+ public void test_Person_withType() throws Exception {
+ assertObjectWithType(new Person(), Person.class);
+ }
+
+ @Test
+ public void test_PersonList() throws Exception {
+ List<Person> args = new ArrayList<Person>();
+ args.add(new Person());
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_PersonSet() throws Exception {
+ Set<Person> args = new HashSet<Person>();
+ args.add(new Person());
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_IntPersonMap() throws Exception {
+ Map<Integer, Person> args = new HashMap<Integer, Person>();
+ args.put(1, new Person());
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_StringPersonMap() throws Exception {
+ Map<String, Person> args = new HashMap<String, Person>();
+ args.put("1", new Person());
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_StringPersonListMap() throws Exception {
+ Map<String, List<Person>> args = new HashMap<String, List<Person>>();
+
+ List<Person> sublist = new ArrayList<Person>();
+ sublist.add(new Person());
+ args.put("1", sublist);
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_PersonListList() throws Exception {
+ List<List<Person>> args = new ArrayList<List<Person>>();
+ List<Person> sublist = new ArrayList<Person>();
+ sublist.add(new Person());
+ args.add(sublist);
+
+ assertObject(args);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationTest.java
new file mode 100644
index 0000000..46ebee3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/AbstractSerializationTest.java
@@ -0,0 +1,1213 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Time;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.model.AnimalEnum;
+import com.alibaba.dubbo.common.model.BizException;
+import com.alibaba.dubbo.common.model.BizExceptionNoDefaultConstructor;
+import com.alibaba.dubbo.common.model.SerializablePerson;
+import com.alibaba.dubbo.common.model.media.Image;
+import com.alibaba.dubbo.common.model.media.Image.Size;
+import com.alibaba.dubbo.common.model.media.Media;
+import com.alibaba.dubbo.common.model.media.Media.Player;
+import com.alibaba.dubbo.common.model.media.MediaContent;
+import com.alibaba.dubbo.common.model.person.BigPerson;
+import com.alibaba.dubbo.common.model.person.FullAddress;
+import com.alibaba.dubbo.common.model.person.PersonInfo;
+import com.alibaba.dubbo.common.model.person.PersonStatus;
+import com.alibaba.dubbo.common.model.person.Phone;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+public abstract class AbstractSerializationTest {
+ Serialization serialization;
+
+ URL url = new URL("protocl", "1.1.1.1", 1234);
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+ static Random random = new Random();
+
+ // ================ Primitive Type ================
+
+ @Test
+ public void test_Bool() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeBool(false);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertFalse(deserialize.readBool());
+
+ try {
+ deserialize.readBool();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_Bool_Multi() throws Exception {
+ boolean[] array = new boolean[100];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = random.nextBoolean();
+ }
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ for (boolean b : array) {
+ objectOutput.writeBool(b);
+ }
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ for (boolean b : array) {
+ assertEquals(b, deserialize.readBool());
+ }
+
+ try {
+ deserialize.readBool();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_Byte() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeByte((byte) 123);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertEquals((byte) 123, deserialize.readByte());
+
+ try {
+ deserialize.readByte();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_Byte_Multi() throws Exception {
+ byte[] array = new byte[100];
+ random.nextBytes(array);
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ for (byte b : array) {
+ objectOutput.writeByte(b);
+ }
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ for (byte b : array) {
+ assertEquals(b, deserialize.readByte());
+ }
+
+ try {
+ deserialize.readByte();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_Short() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeShort((short) 123);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertEquals((short) 123, deserialize.readShort());
+
+ try {
+ deserialize.readShort();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_Integer() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeInt(1);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ int i = deserialize.readInt();
+ assertEquals(1, i);
+
+ try {
+ deserialize.readInt();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_Long() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeLong(123L);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertEquals(123L, deserialize.readLong());
+
+ try {
+ deserialize.readLong();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_Float() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeFloat(1.28F);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertTrue(1.28F == deserialize.readFloat());
+
+ try {
+ deserialize.readFloat();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_Double() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeDouble(1.28);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertTrue(1.28 == deserialize.readDouble());
+
+ try {
+ deserialize.readDouble();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_UtfString() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeUTF("123中华人民共和国");
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertEquals("123中华人民共和国", deserialize.readUTF());
+
+ try {
+ deserialize.readUTF();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_Bytes() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeBytes("123中华人民共和国".getBytes());
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals("123中华人民共和国".getBytes(), deserialize.readBytes());
+
+ try {
+ deserialize.readBytes();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_BytesRange() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeBytes("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, 9);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ byte[] expectedArray = new byte[9];
+ System.arraycopy("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, expectedArray, 0, expectedArray.length);
+ assertArrayEquals(expectedArray, deserialize.readBytes());
+
+ try {
+ deserialize.readBytes();
+ fail();
+ } catch (IOException expected) {}
+ }
+
+ // ================== Util methods ==================
+
+ <T> void assertObjectArray(T[] data, Class<T[]> clazz) throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, clazz.cast(deserialize.readObject()));
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ <T> void assertObjectArrayWithType(T[] data, Class<T[]> clazz) throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, clazz.cast(deserialize.readObject(clazz)));
+
+ try {
+ deserialize.readObject(clazz);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ <T> void assertObject(T data) throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertEquals(data, (T) deserialize.readObject());
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ <T> void assertObjectWithType(T data, Class<T> clazz) throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertEquals(data, (T) deserialize.readObject(clazz));
+
+ try {
+ deserialize.readObject(clazz);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ // ================ Array Type ================
+
+ @Test
+ public void test_boolArray() throws Exception {
+ boolean[] data = new boolean[] { true, false, true};
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject()));
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_boolArray_withType() throws Exception {
+ boolean[] data = new boolean[] { true, false, true};
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject(boolean[].class)));
+
+ try {
+ deserialize.readObject(boolean[].class);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_charArray() throws Exception {
+ char[] data = new char[] { 'a', '中', '无' };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (char[]) deserialize.readObject());
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_charArray_withType() throws Exception {
+ char[] data = new char[] { 'a', '中', '无' };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (char[]) deserialize.readObject(char[].class));
+
+ try {
+ deserialize.readObject(char[].class);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_shortArray() throws Exception {
+ short[] data = new short[] { 37, 39, 12 };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (short[]) deserialize.readObject());
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_shortArray_withType() throws Exception {
+ short[] data = new short[] { 37, 39, 12 };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (short[]) deserialize.readObject(short[].class));
+
+ try {
+ deserialize.readObject(short[].class);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_intArray() throws Exception {
+ int[] data = new int[] { 234, 0, -1};
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (int[]) deserialize.readObject());
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_intArray_withType() throws Exception {
+ int[] data = new int[] { 234, 0, -1};
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (int[]) deserialize.readObject(int[].class));
+
+ try {
+ deserialize.readObject(int[].class);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_longArray() throws Exception {
+ long[] data = new long[] { 234, 0, -1};
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (long[]) deserialize.readObject());
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_longArray_withType() throws Exception {
+ long[] data = new long[] { 234, 0, -1};
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (long[]) deserialize.readObject(long[].class));
+
+ try {
+ deserialize.readObject(long[].class);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_floatArray() throws Exception {
+ float[] data = new float[] { 37F, -3.14F, 123456.7F };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (float[]) deserialize.readObject(), 0.0001F);
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_floatArray_withType() throws Exception {
+ float[] data = new float[] { 37F, -3.14F, 123456.7F };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (float[]) deserialize.readObject(float[].class), 0.0001F);
+
+ try {
+ deserialize.readObject(float[].class);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_doubleArray() throws Exception {
+ double[] data = new double[] { 37D, -3.14D, 123456.7D };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (double[]) deserialize.readObject(), 0.0001);
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_doubleArray_withType() throws Exception {
+ double[] data = new double[] { 37D, -3.14D, 123456.7D };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (double[]) deserialize.readObject(double[].class), 0.0001);
+
+ try {
+ deserialize.readObject(double[].class);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_StringArray() throws Exception {
+ assertObjectArray(new String[] { "1", "b" }, String[].class);
+ }
+
+ @Test
+ public void test_StringArray_withType() throws Exception {
+ assertObjectArrayWithType(new String[] { "1", "b" }, String[].class);
+ }
+
+ @Test
+ public void test_IntegerArray() throws Exception {
+ assertObjectArray(new Integer[] { 234, 0, -1}, Integer[].class);
+ }
+
+ @Test
+ public void test_IntegerArray_withType() throws Exception {
+ assertObjectArrayWithType(new Integer[] { 234, 0, -1}, Integer[].class);
+ }
+
+ @Test
+ public void test_EnumArray() throws Exception {
+ assertObjectArray(new AnimalEnum[] { AnimalEnum.bull, AnimalEnum.cat, AnimalEnum.dog, AnimalEnum.horse}, AnimalEnum[].class);
+ }
+
+ @Test
+ public void test_EnumArray_withType() throws Exception {
+ assertObjectArrayWithType(new AnimalEnum[] { AnimalEnum.bull, AnimalEnum.cat, AnimalEnum.dog, AnimalEnum.horse}, AnimalEnum[].class);
+ }
+
+ // ================ Simple Type ================
+
+ @Test
+ public void test_SPerson() throws Exception {
+ assertObject(new SerializablePerson());
+ }
+
+ @Test
+ public void test_SPerson_withType() throws Exception {
+ assertObjectWithType(new SerializablePerson(), SerializablePerson.class);
+ }
+
+ @Test
+ public void test_BizException() throws Exception {
+ BizException e = new BizException("Hello");
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(e);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ Object read = deserialize.readObject();
+ assertEquals("Hello", ((BizException) read).getMessage());
+ }
+
+ @Test
+ public void test_BizException_WithType() throws Exception {
+ BizException e = new BizException("Hello");
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(e);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ Object read = deserialize.readObject(BizException.class);
+ assertEquals("Hello", ((BizException) read).getMessage());
+ }
+
+ @Test
+ public void test_BizExceptionNoDefaultConstructor() throws Exception {
+ BizExceptionNoDefaultConstructor e = new BizExceptionNoDefaultConstructor("Hello");
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(e);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ Object read = deserialize.readObject();
+ assertEquals("Hello", ((BizExceptionNoDefaultConstructor) read).getMessage());
+ }
+
+ @Test
+ public void test_BizExceptionNoDefaultConstructor_WithType() throws Exception {
+ BizExceptionNoDefaultConstructor e = new BizExceptionNoDefaultConstructor("Hello");
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(e);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ Object read = deserialize.readObject(BizExceptionNoDefaultConstructor.class);
+ assertEquals("Hello", ((BizExceptionNoDefaultConstructor) read).getMessage());
+ }
+
+ @Test
+ public void test_enum() throws Exception {
+ assertObject(AnimalEnum.dog);
+ }
+
+ @Test
+ public void test_enum_withType() throws Exception {
+ assertObjectWithType(AnimalEnum.dog, AnimalEnum.class);
+ }
+
+ @Test
+ public void test_Date() throws Exception {
+ assertObject(new Date());
+ }
+
+ @Test
+ public void test_Date_withType() throws Exception {
+ assertObjectWithType(new Date(), Date.class);
+ }
+
+ @Test
+ public void test_Time() throws Exception {
+ assertObject(new Time(System.currentTimeMillis()));
+ }
+
+ @Test
+ public void test_Time_withType() throws Exception {
+ assertObjectWithType(new Time(System.currentTimeMillis()), Time.class);
+ }
+
+ @Test
+ public void test_ByteWrap() throws Exception {
+ assertObject(new Byte((byte) 12));
+ }
+
+ @Test
+ public void test_ByteWrap_withType() throws Exception {
+ assertObjectWithType(new Byte((byte) 12), Byte.class);
+ }
+
+ @Test
+ public void test_LongWrap() throws Exception {
+ assertObject(new Long(12));
+ }
+
+ @Test
+ public void test_LongWrap_withType() throws Exception {
+ assertObjectWithType(new Long(12), Long.class);
+ }
+
+ @Test
+ public void test_BigInteger() throws Exception {
+ assertObject(new BigInteger("23423434234234234"));
+ }
+
+ @Test
+ public void test_BigInteger_withType() throws Exception {
+ assertObjectWithType(new BigInteger("23423434234234234"), BigInteger.class);
+ }
+
+ @Test
+ public void test_BigDecimal() throws Exception {
+ assertObject(new BigDecimal("23423434234234234.341274832341234235"));
+ }
+
+ @Test
+ public void test_BigDecimal_withType() throws Exception {
+ assertObjectWithType(new BigDecimal("23423434234234234.341274832341234235"), BigDecimal.class);
+ }
+
+ @Test
+ public void test_StringList_asListReturn() throws Exception {
+ List<String> args = Arrays.asList(new String[] { "1", "b" });
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_StringArrayList() throws Exception {
+ List<String> args = new ArrayList<String>(Arrays.asList(new String[] { "1", "b" }));
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_StringSet() throws Exception {
+ Set<String> args = new HashSet<String>();
+ args.add("1");
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_LinkedHashMap() throws Exception {
+ LinkedHashMap<String, String> data = new LinkedHashMap<String, String>();
+ data.put("1", "a");
+ data.put("2", "b");
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ Object read = deserialize.readObject();
+ assertTrue(read instanceof LinkedHashMap);
+ @SuppressWarnings("unchecked")
+ String key1 = ((LinkedHashMap<String, String>)read).entrySet().iterator().next().getKey();
+ assertEquals("1", key1);
+
+ assertEquals(data, read);
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_SPersonList() throws Exception {
+ List<SerializablePerson> args = new ArrayList<SerializablePerson>();
+ args.add(new SerializablePerson());
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_SPersonSet() throws Exception {
+ Set<SerializablePerson> args = new HashSet<SerializablePerson>();
+ args.add(new SerializablePerson());
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_IntSPersonMap() throws Exception {
+ Map<Integer, SerializablePerson> args = new HashMap<Integer, SerializablePerson>();
+ args.put(1, new SerializablePerson());
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_StringSPersonMap() throws Exception {
+ Map<String, SerializablePerson> args = new HashMap<String, SerializablePerson>();
+ args.put("1", new SerializablePerson());
+
+ assertObject(args);
+ }
+
+ // ================ Complex Collection Type ================
+
+ @Test
+ public void test_StringSPersonListMap() throws Exception {
+ Map<String, List<SerializablePerson>> args = new HashMap<String, List<SerializablePerson>>();
+
+ List<SerializablePerson> sublist = new ArrayList<SerializablePerson>();
+ sublist.add(new SerializablePerson());
+ args.put("1", sublist);
+
+ assertObject(args);
+ }
+
+ @Test
+ public void test_SPersonListList() throws Exception {
+ List<List<SerializablePerson>> args = new ArrayList<List<SerializablePerson>>();
+ List<SerializablePerson> sublist = new ArrayList<SerializablePerson>();
+ sublist.add(new SerializablePerson());
+ args.add(sublist);
+
+ assertObject(args);
+ }
+
+ // ================ complex POJO =============
+
+ BigPerson bigPerson;
+ {
+ bigPerson = new BigPerson();
+ bigPerson.setPersonId("superman111");
+ bigPerson.setLoginName("superman");
+ bigPerson.setStatus(PersonStatus.ENABLED);
+ bigPerson.setEmail("sm@1.com");
+ bigPerson.setPenName("pname");
+
+ ArrayList<Phone> phones = new ArrayList<Phone>();
+ Phone phone1 = new Phone("86", "0571", "87654321", "001");
+ Phone phone2 = new Phone("86", "0571", "87654322", "002");
+ phones.add(phone1);
+ phones.add(phone2);
+
+ PersonInfo pi = new PersonInfo();
+ pi.setPhones(phones);
+ Phone fax = new Phone("86", "0571", "87654321", null);
+ pi.setFax(fax);
+ FullAddress addr = new FullAddress("CN", "zj", "3480", "wensanlu", "315000");
+ pi.setFullAddress(addr);
+ pi.setMobileNo("13584652131");
+ pi.setMale(true);
+ pi.setDepartment("b2b");
+ pi.setHomepageUrl("www.capcom.com");
+ pi.setJobTitle("qa");
+ pi.setName("superman");
+
+ bigPerson.setInfoProfile(pi);
+ }
+
+ @Test
+ public void test_BigPerson() throws Exception {
+ assertObject(bigPerson);
+ }
+
+ @Test
+ public void test_BigPerson_WithType() throws Exception {
+ assertObjectWithType(bigPerson, BigPerson.class);
+ }
+
+ MediaContent mediaContent;
+ {
+ Media media = new Media();
+ media.setUri("uri://中华人民共和国");
+ media.setTitle("title");
+ media.setWidth(1239);
+ media.setHeight(1938);
+ media.setFormat("format-xxxx");
+ media.setDuration(93419235);
+ media.setSize(3477897);
+ media.setBitrate(94523);
+ List<String> persons = new ArrayList<String>();
+ persons.add("jerry");
+ persons.add("tom");
+ persons.add("lucy");
+ media.setPersons(persons);
+ media.setCopyright("1999-2011");
+ media.setPlayer(Player.FLASH);
+
+ List<Image> images = new ArrayList<Image>();
+ for(int i = 0; i < 10; ++i) {
+ Image image = new Image();
+ image.setUri("url" + i);
+ if(i % 2 == 0) image.setTitle("title" + i);
+ image.setWidth(34 + i);
+ image.setHeight(2323 + i);
+ image.setSize((i % 2 == 0) ? Size.SMALL : Size.LARGE);
+
+ images.add(image);
+ }
+
+ mediaContent = new MediaContent(media, images);
+ }
+
+ @Test
+ public void test_MediaContent() throws Exception {
+ assertObject(mediaContent);
+ }
+
+ @Test
+ public void test_MediaContent_WithType() throws Exception {
+ assertObjectWithType(mediaContent, MediaContent.class);
+ }
+
+ @Test
+ public void test_MultiObject() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeBool(false);
+ objectOutput.writeObject(bigPerson);
+ objectOutput.writeByte((byte) 23);
+ objectOutput.writeObject(mediaContent);
+ objectOutput.writeInt(-23);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertEquals(false, deserialize.readBool());
+ assertEquals(bigPerson, deserialize.readObject());
+ assertEquals((byte) 23, deserialize.readByte());
+ assertEquals(mediaContent, deserialize.readObject());
+ assertEquals(-23, deserialize.readInt());
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ @Test
+ public void test_MultiObject_WithType() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeBool(false);
+ objectOutput.writeObject(bigPerson);
+ objectOutput.writeByte((byte) 23);
+ objectOutput.writeObject(mediaContent);
+ objectOutput.writeInt(-23);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertEquals(false, deserialize.readBool());
+ assertEquals(bigPerson, deserialize.readObject(BigPerson.class));
+ assertEquals((byte) 23, deserialize.readByte());
+ assertEquals(mediaContent, deserialize.readObject(MediaContent.class));
+ assertEquals(-23, deserialize.readInt());
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+
+ // abnormal case
+
+ @Test
+ public void test_MediaContent_badStream() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(mediaContent);
+ objectOutput.flushBuffer();
+
+ byte[] byteArray = byteArrayOutputStream.toByteArray();
+ for (int i = 0; i < byteArray.length; i++) {
+ if(i%3 == 0) {
+ byteArray[i] = (byte)~byteArray[i];
+ }
+ }
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+
+ try {
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+ @SuppressWarnings("unused") // local variable, convenient for debug
+ Object read = deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ System.out.println(expected);
+ }
+ }
+
+ @Test
+ public void test_MediaContent_WithType_badStream() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(mediaContent);
+ objectOutput.flushBuffer();
+
+ byte[] byteArray = byteArrayOutputStream.toByteArray();
+ for (int i = 0; i < byteArray.length; i++) {
+ if(i%3 == 0) {
+ byteArray[i] = (byte)~byteArray[i];
+ }
+ }
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+
+ try {
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+ @SuppressWarnings("unused") // local variable, convenient for debug
+ Object read = deserialize.readObject(MediaContent.class);
+ fail();
+ } catch (IOException expected) {
+ System.out.println(expected);
+ }
+ }
+
+
+ @Test(timeout=3000)
+ public void test_LoopReference() throws Exception {
+ Map<String, Object> map= new HashMap<String, Object>();
+ map.put("k1", "v1");
+ map.put("self", map);
+
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(map);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> output = (Map<String, Object>) deserialize.readObject();
+
+ assertEquals("v1", output.get("k1"));
+ assertSame(output, output.get("self"));
+ }
+
+ // ================ final field test ================
+
+ @Test
+ public void test_URL_mutable_withType() throws Exception {
+ URL data = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ URL actual = (URL) deserialize.readObject(URL.class);
+ assertEquals(data, actual);
+ assertEquals(data.getParameters(), actual.getParameters());
+
+ try {
+ deserialize.readObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/CompactedJavaSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/CompactedJavaSerializationTest.java
new file mode 100644
index 0000000..951ebce
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/CompactedJavaSerializationTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import com.alibaba.dubbo.common.serialize.support.java.CompactedJavaSerialization;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class CompactedJavaSerializationTest extends AbstractSerializationPersionFailTest {
+ {
+ serialization = new CompactedJavaSerialization();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/DubboSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/DubboSerializationTest.java
new file mode 100644
index 0000000..f497a16
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/DubboSerializationTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.serialize.support.dubbo.DubboSerialization;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class DubboSerializationTest extends AbstractSerializationPersionFailTest {
+ {
+ serialization = new DubboSerialization();
+ }
+
+ /**
+ * @desc:DubboSerialization error: java.lang.IllegalAccessError: tried to access class java.util.Arrays$ArrayList from class com.alibaba.dubbo.common.serialize.support.dubbo.Builder$bc6
+ * @reason:in writeObject method, the first line is :java.util.Arrays$ArrayList v = (java.util.Arrays$ArrayList)$1; java.util.Arrays$ArrayList is a inter static class ,can not access.
+ * @tradeoff: how to resolve:we need change writeObject method, replace the first line with java.util.ArrayList v = new java.util.ArrayList((List)$1) , and in the same time, modify the defaultArg method ,return special construct args for ArrayList ... too ugly to support.
+ */
+ @Ignore
+ @Test
+ public void test_StringList_asListReturn() throws Exception {
+ super.test_StringList_asListReturn();
+ }
+
+ // FIXME
+ @Ignore("StackOverflowError")
+ @Test(timeout=3000)
+ public void test_LoopReference() throws Exception {}
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FastJsonSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FastJsonSerializationTest.java
new file mode 100644
index 0000000..cd30285
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FastJsonSerializationTest.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.model.media.MediaContent;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.support.json.FastJsonSerialization;
+import com.alibaba.fastjson.JSONException;
+
+/**
+ * FIXME FastJson Serialization的失败,被暂时被忽略
+ *
+ * @author ding.lid
+ */
+public class FastJsonSerializationTest extends AbstractSerializationPersionOkTest {
+ {
+ serialization = new FastJsonSerialization();
+ }
+
+ @Ignore // FIXME
+ @Test
+ public void test_BytesRange() throws Exception {}
+
+ @Ignore("bool[] type missing to JSONArray")
+ @Test
+ public void test_boolArray() throws Exception {}
+
+ @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: create asm serilizer error, class char")
+ @Test
+ public void test_charArray() throws Exception {}
+
+ @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: create asm serilizer error, class char")
+ @Test
+ public void test_charArray_withType() throws Exception {}
+
+ @Ignore("short[] type missing to JSONArray")
+ @Test
+ public void test_shortArray() throws Exception {}
+
+ @Ignore("int[] type missing to JSONArray")
+ @Test
+ public void test_intArray() throws Exception {}
+
+ @Ignore("long[] type missing to JSONArray")
+ @Test
+ public void test_longArray() throws Exception {}
+
+ @Ignore("float[] type missing to JSONArray")
+ @Test
+ public void test_floatArray() throws Exception {}
+
+ @Ignore("double[] type missing to JSONArray")
+ @Test
+ public void test_doubleArray() throws Exception {}
+
+ @Ignore("String[] type missing to JSONArray")
+ @Test
+ public void test_StringArray() throws Exception {}
+
+ @Ignore("Integer[] type missing to JSONArray")
+ @Test
+ public void test_IntegerArray() throws Exception {}
+
+ @Ignore("Integer[] type missing to JSONArray")
+ @Test
+ public void test_EnumArray() throws Exception {}
+
+ @Ignore("type mising to Long")
+ @Test
+ public void test_Date() throws Exception {}
+
+ @Ignore("type mising to Long")
+ @Test
+ public void test_Time() throws Exception {}
+
+ @Ignore("com.alibaba.fastjson.JSONException: create asm deserializer error, java.sql.Time")
+ @Test
+ public void test_Time_withType() throws Exception {}
+
+ @Ignore("type mising to Integer")
+ @Test
+ public void test_ByteWrap() throws Exception {}
+
+ @Ignore("type mising to Integer")
+ @Test
+ public void test_LongWrap() throws Exception {}
+
+ @Ignore("type mising to Long")
+ @Test
+ public void test_BigInteger() throws Exception {}
+
+ @Ignore("SPerson type missing")
+ @Test
+ public void test_SPerson() throws Exception {}
+
+ @Ignore("BizException type missing to Map")
+ @Test
+ public void test_BizException() throws Exception {}
+
+ @Ignore("BizExceptionNoDefaultConstructor type missing to Map")
+ @Test
+ public void test_BizExceptionNoDefaultConstructor() throws Exception {}
+
+ // FIXME 没有缺省构造函数失败
+ @Ignore("NoDefaultConstructor")
+ @Test
+ public void test_BizExceptionNoDefaultConstructor_WithType() throws Exception {}
+
+ @Ignore("Enum type missing to String")
+ @Test
+ public void test_enum() throws Exception {}
+
+ @Ignore("String set missing to JSONArray")
+ @Test
+ public void test_StringSet() throws Exception {}
+
+ @Ignore("LinkedHashMap type missing to Map")
+ @Test
+ public void test_LinkedHashMap() throws Exception {}
+
+
+ @Ignore("person type missing")
+ @Test
+ public void test_SPersonList() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_SPersonSet() throws Exception {}
+
+ @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: illegal identifier : 1")
+ @Test
+ public void test_IntSPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringSPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringSPersonListMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_SPersonListList() throws Exception {}
+
+ @Ignore("BigPerson type missing")
+ @Test
+ public void test_BigPerson() throws Exception {}
+
+ @Ignore("MediaContent type missing")
+ @Test
+ public void test_MediaContent() throws Exception {}
+
+ @Ignore("type missing")
+ @Test
+ public void test_MultiObject() throws Exception {}
+
+ @Test
+ public void test_MediaContent_badStream() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(mediaContent);
+ objectOutput.flushBuffer();
+
+ byte[] byteArray = byteArrayOutputStream.toByteArray();
+ for (int i = 0; i < byteArray.length; i++) {
+ if(i%3 == 0) {
+ byteArray[i] = (byte)~byteArray[i];
+ }
+ }
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+
+ try {
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+ @SuppressWarnings("unused") // local variable, convenient for debug
+ Object read = deserialize.readObject();
+ fail();
+ } catch (JSONException expected) {
+ System.out.println(expected);
+ }
+ }
+
+ @Test
+ public void test_MediaContent_WithType_badStream() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(mediaContent);
+ objectOutput.flushBuffer();
+
+ byte[] byteArray = byteArrayOutputStream.toByteArray();
+ for (int i = 0; i < byteArray.length; i++) {
+ if(i%3 == 0) {
+ byteArray[i] = (byte)~byteArray[i];
+ }
+ }
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+
+ try {
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+ @SuppressWarnings("unused") // local variable, convenient for debug
+ Object read = deserialize.readObject(MediaContent.class);
+ fail();
+ } catch (JSONException expected) {
+ System.out.println(expected);
+ }
+ }
+
+ // FIXME DUBBO-63
+ @Ignore
+ @Test
+ public void test_URL_mutable_withType() throws Exception {}
+
+ @Ignore
+ @Test(timeout=3000)
+ public void test_LoopReference() throws Exception {}
+
+ // ========== Person
+
+ @Ignore("person type missing")
+ @Test
+ public void test_Person() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_PersonList() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_PersonSet() throws Exception {}
+
+ @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: illegal identifier : 1")
+ @Test
+ public void test_IntPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringPersonListMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_PersonListList() throws Exception {}
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/Hessian2SerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/Hessian2SerializationTest.java
new file mode 100644
index 0000000..1da0e74
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/Hessian2SerializationTest.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.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("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..735c5e6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CollectionUtilsTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import static org.junit.Assert.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_joinList() throws Exception {
+ List<String> list = Arrays.asList();
+ assertEquals("", CollectionUtils.join(list, "/"));
+
+ list = Arrays.asList("x");
+ assertEquals("x", CollectionUtils.join(list, "-"));
+
+ list = Arrays.asList("a", "b");
+ assertEquals("a/b", CollectionUtils.join(list, "/"));
+ }
+
+ @Test
+ public void test_mapEquals() throws Exception {
+ assertTrue(CollectionUtils.mapEquals(null, null));
+ assertFalse(CollectionUtils.mapEquals(null, new HashMap<String, String>()));
+ assertFalse(CollectionUtils.mapEquals(new HashMap<String, String>(), null));
+
+ assertTrue(CollectionUtils.mapEquals(CollectionUtils.toStringMap("1", "a", "2", "b"), CollectionUtils.toStringMap("1", "a", "2", "b")));
+ assertFalse(CollectionUtils.mapEquals(CollectionUtils.toStringMap("1", "a"), CollectionUtils.toStringMap("1", "a", "2", "b")));
+ }
+
+ @Test
+ public void test_toMap() throws Exception {
+ assertTrue(CollectionUtils.toMap().isEmpty());
+
+
+ Map<String, Integer> expected = new HashMap<String, Integer>();
+ expected.put("a", 1);
+ expected.put("b", 2);
+ expected.put("c", 3);
+
+ assertEquals(expected, CollectionUtils.toMap("a", 1, "b", 2, "c", 3));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtilsTest.java
new file mode 100644
index 0000000..6fe0b72
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtilsTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.junit.Test;
+
+public class CompatibleTypeUtilsTest {
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testCompatibleTypeConvert() throws Exception {
+ Object result;
+
+ {
+ Object input = new Object();
+ result = CompatibleTypeUtils.compatibleTypeConvert(input, Date.class);
+ assertSame(input, result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(input, null);
+ assertSame(input, result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(null, Date.class);
+ assertNull(result);
+ }
+
+ {
+ result = CompatibleTypeUtils.compatibleTypeConvert("a", char.class);
+ assertEquals(Character.valueOf('a'), (Character) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert("A", MyEnum.class);
+ assertEquals(MyEnum.A, (MyEnum) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert("3", BigInteger.class);
+ assertEquals(new BigInteger("3"), (BigInteger) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert("3", BigDecimal.class);
+ assertEquals(new BigDecimal("3"), (BigDecimal) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11 12:24:12", Date.class);
+ assertEquals(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2011-12-11 12:24:12"), (Date) result);
+ }
+
+ {
+ result = CompatibleTypeUtils.compatibleTypeConvert(3, byte.class);
+ assertEquals(Byte.valueOf((byte)3), (Byte) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert((byte)3, int.class);
+ assertEquals(Integer.valueOf(3), (Integer) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(3, short.class);
+ assertEquals(Short.valueOf((short)3), (Short) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert((short)3, int.class);
+ assertEquals(Integer.valueOf(3), (Integer) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(3, int.class);
+ assertEquals(Integer.valueOf(3), (Integer) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(3, long.class);
+ assertEquals(Long.valueOf(3), (Long) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(3L, int.class);
+ assertEquals(Integer.valueOf(3), (Integer) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(3L, BigInteger.class);
+ assertEquals(BigInteger.valueOf(3L), (BigInteger) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(BigInteger.valueOf(3L), int.class);
+ assertEquals(Integer.valueOf(3), (Integer) result);
+ }
+
+ {
+ result = CompatibleTypeUtils.compatibleTypeConvert(3D, float.class);
+ assertEquals(Float.valueOf(3), (Float) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(3F, double.class);
+ assertEquals(Double.valueOf(3), (Double) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(3D, double.class);
+ assertEquals(Double.valueOf(3), (Double) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(3D, BigDecimal.class);
+ assertEquals(BigDecimal.valueOf(3D), (BigDecimal) result);
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(BigDecimal.valueOf(3D), double.class);
+ assertEquals(Double.valueOf(3), (Double) result);
+ }
+
+ {
+ List<String> list = new ArrayList<String>();
+ list.add("a");
+ list.add("b");
+
+ Set<String> set = new HashSet<String>();
+ set.add("a");
+ set.add("b");
+
+ String[] array = new String[] {"a", "b"};
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(array, List.class);
+ assertEquals(ArrayList.class, result.getClass());
+ assertEquals(2, ((List<String>)result).size());
+ assertTrue(((List<String>)result).contains("a"));
+ assertTrue(((List<String>)result).contains("b"));
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(set, List.class);
+ assertEquals(ArrayList.class, result.getClass());
+ assertEquals(2, ((List<String>)result).size());
+ assertTrue(((List<String>)result).contains("a"));
+ assertTrue(((List<String>)result).contains("b"));
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(array, CopyOnWriteArrayList.class);
+ assertEquals(CopyOnWriteArrayList.class, result.getClass());
+ assertEquals(2, ((List<String>)result).size());
+ assertTrue(((List<String>)result).contains("a"));
+ assertTrue(((List<String>)result).contains("b"));
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(set, CopyOnWriteArrayList.class);
+ assertEquals(CopyOnWriteArrayList.class, result.getClass());
+ assertEquals(2, ((List<String>)result).size());
+ assertTrue(((List<String>)result).contains("a"));
+ assertTrue(((List<String>)result).contains("b"));
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(set, String[].class);
+ assertEquals(String[].class, result.getClass());
+ assertEquals(2, ((String[])result).length);
+ assertTrue(((String[])result)[0].equals("a") || ((String[])result)[0].equals("b"));
+ assertTrue(((String[])result)[1].equals("a") || ((String[])result)[1].equals("b"));
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(array, Set.class);
+ assertEquals(HashSet.class, result.getClass());
+ assertEquals(2, ((Set<String>)result).size());
+ assertTrue(((Set<String>)result).contains("a"));
+ assertTrue(((Set<String>)result).contains("b"));
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(list, Set.class);
+ assertEquals(HashSet.class, result.getClass());
+ assertEquals(2, ((Set<String>)result).size());
+ assertTrue(((Set<String>)result).contains("a"));
+ assertTrue(((Set<String>)result).contains("b"));
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(array, ConcurrentHashSet.class);
+ assertEquals(ConcurrentHashSet.class, result.getClass());
+ assertEquals(2, ((Set<String>)result).size());
+ assertTrue(((Set<String>)result).contains("a"));
+ assertTrue(((Set<String>)result).contains("b"));
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(list, ConcurrentHashSet.class);
+ assertEquals(ConcurrentHashSet.class, result.getClass());
+ assertEquals(2, ((Set<String>)result).size());
+ assertTrue(((Set<String>)result).contains("a"));
+ assertTrue(((Set<String>)result).contains("b"));
+
+ result = CompatibleTypeUtils.compatibleTypeConvert(list, String[].class);
+ assertEquals(String[].class, result.getClass());
+ assertEquals(2, ((String[])result).length);
+ assertTrue(((String[])result)[0].equals("a"));
+ assertTrue(((String[])result)[1].equals("b"));
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ConfigUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ConfigUtilsTest.java
new file mode 100644
index 0000000..18e266e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ConfigUtilsTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ * @author tony.chenl
+ */
+public class ConfigUtilsTest {
+
+ public static <T> List<T> toArray(T... args) {
+ List<T> ret = new ArrayList<T>();
+ for(T a : args) {
+ ret.add(a);
+ }
+ return ret;
+ }
+
+ /**
+ * 测试点1:用户配置参数在最后 测试点2:用户配置参数如果带-,会删除同名的默认参数 测试点3:default开头的默认参数会被删除
+ */
+ @Test
+ public void testMergeValues() {
+ List<String> merged = ConfigUtils.mergeValues(Serialization.class, "aaa,bbb,default.cunstom",
+ toArray("dubbo","default.hessian2","json"));
+ Assert.assertEquals(toArray("dubbo", "json", "aaa", "bbb", "default.cunstom"), merged);
+ }
+
+ /**
+ * 测试点1:用户配置参数在最后 测试点2:用户配置参数如果带-,会删除同名的默认参数 测试点3:default开头的默认参数会被删除
+ */
+ @Test
+ public void testMergeValues_addDefault() {
+ List<String> merged = ConfigUtils.mergeValues(Serialization.class, "aaa,bbb,default,zzz",
+ toArray("dubbo","default.hessian2","json"));
+ Assert.assertEquals(toArray("aaa", "bbb","dubbo", "json", "zzz"), merged);
+ }
+
+ /**
+ * 测试点1:用户配置-default,会删除所有默认参数
+ */
+ @Test
+ public void testMergeValuesDeleteDefault() {
+ List<String> merged = ConfigUtils.mergeValues(Serialization.class, "-default", toArray("dubbo","default.hessian2","json"));
+ Assert.assertEquals(toArray(), merged);
+ }
+
+ /**
+ * 测试点1:用户配置-default,会删除所有默认参数
+ */
+ @Test
+ public void testMergeValuesDeleteDefault_2() {
+ List<String> merged = ConfigUtils.mergeValues(Serialization.class, "-default,aaa", toArray("dubbo","default.hessian2","json"));
+ Assert.assertEquals(toArray("aaa"), merged);
+ }
+
+ /**
+ * 测试点1:用户配置-default,会删除所有默认参数
+ */
+ @Test
+ public void testMergeValuesDelete() {
+ List<String> merged = ConfigUtils.mergeValues(Serialization.class, "-dubbo,aaa", toArray("dubbo","default.hessian2","json"));
+ Assert.assertEquals(toArray("json", "aaa"), merged);
+ }
+
+ @Test
+ public void test_loadProperties_noFile() throws Exception {
+ Properties p = ConfigUtils.loadProperties("notExisted", true);
+ Properties expected = new Properties();
+ Assert.assertEquals(expected, p);
+
+ p = ConfigUtils.loadProperties("notExisted", false);
+ Assert.assertEquals(expected, p);
+ }
+
+ @Test
+ public void test_loadProperties_oneFile() throws Exception {
+ Properties p = ConfigUtils.loadProperties("properties.load", false);
+
+ Properties expected = new Properties();
+ expected.put("a", "12");
+ expected.put("b", "34");
+ expected.put("c", "56");
+
+ Assert.assertEquals(expected, p);
+ }
+
+ @Test
+ public void test_loadProperties_oneFile_allowMulti() throws Exception {
+ Properties p = ConfigUtils.loadProperties("properties.load", true);
+
+ Properties expected = new Properties();
+ expected.put("a", "12");
+ expected.put("b", "34");
+ expected.put("c", "56");
+
+ Assert.assertEquals(expected, p);
+ }
+
+ @Test
+ public void test_loadProperties_oneFile_notRootPath() throws Exception {
+ Properties p = ConfigUtils.loadProperties("META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool", false);
+
+ Properties expected = new Properties();
+ expected.put("fixed", "com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool");
+ expected.put("cached", "com.alibaba.dubbo.common.threadpool.support.cached.CachedThreadPool");
+
+ Assert.assertEquals(expected, p);
+ }
+
+
+ @Ignore("see http://code.alibabatech.com/jira/browse/DUBBO-133")
+ @Test
+ public void test_loadProperties_multiFile_notRootPath_Exception() throws Exception {
+ try {
+ ConfigUtils.loadProperties("META-INF/services/com.alibaba.dubbo.common.status.StatusChecker", false);
+ Assert.fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("only 1 META-INF/services/com.alibaba.dubbo.common.status.StatusChecker file is expected, but 2 dubbo.properties files found on class path:"));
+ }
+ }
+
+ @Test
+ public void test_loadProperties_multiFile_notRootPath() throws Exception {
+
+ Properties p = ConfigUtils.loadProperties("META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker", true);
+
+ Properties expected = new Properties();
+ expected.put("memory", "com.alibaba.dubbo.common.status.support.MemoryStatusChecker");
+ expected.put("load", "com.alibaba.dubbo.common.status.support.LoadStatusChecker");
+ expected.put("aa", "12");
+
+ Assert.assertEquals(expected, p);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/MyEnum.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/MyEnum.java
new file mode 100644
index 0000000..1eb96ce
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/MyEnum.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+/**
+ * MyEnum
+ *
+ * @author william.liangf
+ */
+public enum MyEnum {
+
+ A, B
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/NetUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/NetUtilsTest.java
new file mode 100644
index 0000000..fcf12f3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/NetUtilsTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+public class NetUtilsTest extends TestCase {
+
+ public void testValidAddress() throws Exception {
+ Assert.assertTrue(NetUtils.isValidAddress("10.20.130.230:20880"));
+ Assert.assertFalse(NetUtils.isValidAddress("10.20.130.230"));
+ Assert.assertFalse(NetUtils.isValidAddress("10.20.130.230:666666"));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ParametersTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ParametersTest.java
new file mode 100644
index 0000000..ae006af
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ParametersTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class ParametersTest extends TestCase
+{
+ final String ServiceName = "com.alibaba.dubbo.rpc.service.GenericService";
+ final String ServiceVersion = "1.0.15";
+ final String LoadBalance = "lcr";
+
+ public void testMap2Parameters() throws Exception
+ {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("name", "com.alibaba.dubbo.rpc.service.GenericService");
+ map.put("version", "1.0.15");
+ map.put("lb", "lcr");
+ map.put("max.active", "500");
+ assertEquals(map.get("name"), ServiceName);
+ assertEquals(map.get("version"), ServiceVersion);
+ assertEquals(map.get("lb"), LoadBalance);
+ }
+
+ public void testString2Parameters() throws Exception
+ {
+ String qs = "name=com.alibaba.dubbo.rpc.service.GenericService&version=1.0.15&lb=lcr";
+ Map<String, String> map = StringUtils.parseQueryString(qs);
+ assertEquals(map.get("name"), ServiceName);
+ assertEquals(map.get("version"), ServiceVersion);
+ assertEquals(map.get("lb"), LoadBalance);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/PojoUtilsTest.java
new file mode 100644
index 0000000..2dccdf6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/PojoUtilsTest.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+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_Map_List_pojo() throws Exception {
+ Map<String, List<Object>> map = new HashMap<String, List<Object>>();
+
+ List<Object> list = new ArrayList<Object>();
+ list.add(new Person());
+ list.add(new SerializablePerson());
+
+ map.put("k", list);
+
+ Object generalize = PojoUtils.generalize(map);
+ Object realize = PojoUtils.realize(generalize, Map.class);
+ assertEquals(map, realize);
+ }
+
+ @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 class Parent {
+ String name;
+
+ int age;
+
+ Child child;
+
+ public static Parent getNewParent() {
+ return new Parent();
+ }
+
+ 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 Child getChild() {
+ return child;
+ }
+
+ public void setChild(Child child) {
+ this.child = child;
+ }
+ }
+
+ public static class Child {
+ String toy;
+
+ public String getToy() {
+ return toy;
+ }
+
+ public void setToy(String toy) {
+ this.toy = toy;
+ }
+
+ public Parent getParent() {
+ return parent;
+ }
+
+ public void setParent(Parent parent) {
+ this.parent = parent;
+ }
+
+ Parent parent;
+ }
+
+ @Test
+ public void test_Loop_pojo() throws Exception {
+ Parent p = new Parent();
+ p.setAge(10);
+ p.setName("jerry");
+
+ Child c = new Child();
+ c.setToy("haha");
+
+ p.setChild(c);
+ c.setParent(p);
+
+ Object generalize = PojoUtils.generalize(p);
+ Parent parent = (Parent) PojoUtils.realize(generalize, Parent.class);
+
+ assertEquals(10, parent.getAge());
+ assertEquals("jerry", parent.getName());
+
+ assertEquals("haha", parent.getChild().getToy());
+ assertSame(parent, parent.getChild().getParent());
+ }
+
+ @Test
+ public void test_Loop_Map() throws Exception {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ map.put("k", "v");
+ map.put("m", map);
+
+ Object generalize = PojoUtils.generalize(map);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> ret = (Map<String, Object>) PojoUtils.realize(generalize, Map.class);
+
+ assertEquals("v", ret.get("k"));
+ assertSame(ret, ret.get("m"));
+ }
+
+ @Test
+ public void test_LoopPojoInMap() throws Exception {
+ Parent p = new Parent();
+ p.setAge(10);
+ p.setName("jerry");
+
+ Child c = new Child();
+ c.setToy("haha");
+
+ p.setChild(c);
+ c.setParent(p);
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("k", p);
+
+ Object generalize = PojoUtils.generalize(map);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> realize = (Map<String, Object>) PojoUtils.realize(generalize, Map.class, getType("getMapGenericType"));
+
+ Parent parent = (Parent) realize.get("k");
+
+ assertEquals(10, parent.getAge());
+ assertEquals("jerry", parent.getName());
+
+ assertEquals("haha", parent.getChild().getToy());
+ assertSame(parent, parent.getChild().getParent());
+ }
+
+ @Test
+ public void test_LoopPojoInList() throws Exception {
+ Parent p = new Parent();
+ p.setAge(10);
+ p.setName("jerry");
+
+ Child c = new Child();
+ c.setToy("haha");
+
+ p.setChild(c);
+ c.setParent(p);
+
+ List<Object> list = new ArrayList<Object>();
+ list.add(p);
+
+ Object generalize = PojoUtils.generalize(list);
+ @SuppressWarnings("unchecked")
+ List<Object> realize = (List<Object>) PojoUtils.realize(generalize, List.class, getType("getListGenericType"));
+
+ Parent parent = (Parent) realize.get(0);
+
+ assertEquals(10, parent.getAge());
+ assertEquals("jerry", parent.getName());
+
+ assertEquals("haha", parent.getChild().getToy());
+ assertSame(parent, parent.getChild().getParent());
+ }
+
+ @Test
+ public void test_PojoInList() throws Exception {
+ Parent p = new Parent();
+ p.setAge(10);
+ p.setName("jerry");
+
+ List<Object> list = new ArrayList<Object>();
+ list.add(p);
+
+ Object generalize = PojoUtils.generalize(list);
+ @SuppressWarnings("unchecked")
+ List<Object> realize = (List<Object>) PojoUtils.realize(generalize, List.class, getType("getListGenericType"));
+
+ Parent parent = (Parent) realize.get(0);
+
+ assertEquals(10, parent.getAge());
+ assertEquals("jerry", parent.getName());
+ }
+
+ public void setLong(long l){}
+
+ public void setInt(int l){}
+
+ public List<Parent> getListGenericType(){return null;};
+ public Map<String, Parent> getMapGenericType(){return null;};
+
+ // java.lang.IllegalArgumentException: argument type mismatch
+ @Test
+ public void test_realize_LongPararmter_IllegalArgumentException() throws Exception {
+ Method method = PojoUtilsTest.class.getMethod("setLong", long.class);
+ assertNotNull(method);
+
+ Object value = PojoUtils.realize("563439743927993", method.getParameterTypes()[0], method.getGenericParameterTypes()[0]);
+
+ method.invoke(new PojoUtilsTest(), value);
+ }
+
+ // java.lang.IllegalArgumentException: argument type mismatch
+ @Test
+ public void test_realize_IntPararmter_IllegalArgumentException() throws Exception {
+ Method method = PojoUtilsTest.class.getMethod("setInt", int.class);
+ assertNotNull(method);
+
+ Object value = PojoUtils.realize("123", method.getParameterTypes()[0], method.getGenericParameterTypes()[0]);
+
+ method.invoke(new PojoUtilsTest(), value);
+ }
+
+ @Test
+ public void testStackOverflow() throws Exception {
+ Parent parent = Parent.getNewParent();
+ parent.setAge(Integer.MAX_VALUE);
+ String name = UUID.randomUUID().toString();
+ parent.setName(name);
+ Object generalize = PojoUtils.generalize(parent);
+ assertTrue(generalize instanceof Map);
+ Map map = (Map) generalize;
+ assertEquals(Integer.MAX_VALUE, map.get("age"));
+ assertEquals(name, map.get("name"));
+
+ Parent realize = (Parent)PojoUtils.realize(generalize, Parent.class);
+ assertEquals(Integer.MAX_VALUE, realize.getAge());
+ assertEquals(name, realize.getName());
+ }
+
+ @Test
+ public void testGenerializeAndRealizeClass() throws Exception {
+ Object generalize = PojoUtils.generalize(Integer.class);
+ assertEquals(Integer.class.getName(), generalize);
+ Object real = PojoUtils.realize(generalize, Integer.class.getClass());
+ assertEquals(Integer.class, real);
+
+ generalize = PojoUtils.generalize(int[].class);
+ assertEquals(int[].class.getName(), generalize);
+ real = PojoUtils.realize(generalize, int[].class.getClass());
+ assertEquals(int[].class, real);
+ }
+}
\ 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..5e54475
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ReflectUtilsTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.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.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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"));
+ }
+ }
+
+ @Test
+ public void test_getEmptyObject() throws Exception {
+ assertTrue(ReflectUtils.getEmptyObject(Collection.class) instanceof Collection);
+ assertTrue(ReflectUtils.getEmptyObject(List.class) instanceof List);
+ assertTrue(ReflectUtils.getEmptyObject(Set.class) instanceof Set);
+ assertTrue(ReflectUtils.getEmptyObject(Map.class) instanceof Map);
+ assertTrue(ReflectUtils.getEmptyObject(Object[].class) instanceof Object[]);
+ assertEquals(ReflectUtils.getEmptyObject(String.class), "");
+ assertEquals(ReflectUtils.getEmptyObject(short.class), Short.valueOf((short)0));
+ assertEquals(ReflectUtils.getEmptyObject(byte.class), Byte.valueOf((byte)0));
+ assertEquals(ReflectUtils.getEmptyObject(int.class), Integer.valueOf(0));
+ assertEquals(ReflectUtils.getEmptyObject(long.class), Long.valueOf(0));
+ assertEquals(ReflectUtils.getEmptyObject(float.class), Float.valueOf(0));
+ assertEquals(ReflectUtils.getEmptyObject(double.class), Double.valueOf(0));
+ assertEquals(ReflectUtils.getEmptyObject(char.class), Character.valueOf('\0'));
+ assertEquals(ReflectUtils.getEmptyObject(boolean.class), Boolean.FALSE);
+ EmptyClass object = (EmptyClass) ReflectUtils.getEmptyObject(EmptyClass.class);
+ assertNotNull(object);
+ assertNotNull(object.getProperty());
+ }
+
+ public static class EmptyClass {
+
+ private EmptyProperty property;
+
+ public EmptyProperty getProperty() {
+ return property;
+ }
+
+ public void setProperty(EmptyProperty property) {
+ this.property = property;
+ }
+
+ }
+
+ public static class EmptyProperty {
+ }
+
+ static class TestedClass {
+ public void method1(int x) {
+ }
+
+ public void overrideMethod(int x) {
+ }
+
+ public void overrideMethod(Integer x) {
+ }
+
+ public void overrideMethod(String s) {
+ }
+
+ public void overrideMethod(String s1, String s2) {
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.java
new file mode 100644
index 0000000..da816f6
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+public class StringUtilsTest extends TestCase
+{
+ public void testJoin() throws Exception
+ {
+ String[] s = {"1","2","3"};
+ assertEquals(StringUtils.join(s), "123");
+ assertEquals(StringUtils.join(s, ','), "1,2,3");
+ }
+
+ public void testSplit() throws Exception
+ {
+ String s = "d,1,2,4";
+ assertEquals(StringUtils.split(s, ',').length, 4);
+ }
+
+ public void testTranslat() throws Exception
+ {
+ String s = "16314";
+ assertEquals(StringUtils.translat(s, "123456", "abcdef"), "afcad");
+ assertEquals(StringUtils.translat(s, "123456", "abcd"), "acad");
+ }
+
+ public void testJoin_Colletion_String() throws Exception
+ {
+ List<String> list = new ArrayList<String>();
+ assertEquals("", StringUtils.join(list, ","));
+
+ list.add("v1");
+ assertEquals("v1", StringUtils.join(list, "-"));
+
+ list.add("v2");
+ list.add("v3");
+ String out = StringUtils.join(list, ":");
+ assertEquals("v1:v2:v3", out);
+ }
+
+ @Test
+ public void testCamelToSplitName() throws Exception
+ {
+ assertEquals("ab-cd-ef", StringUtils.camelToSplitName("abCdEf", "-"));
+ assertEquals("ab-cd-ef", StringUtils.camelToSplitName("AbCdEf", "-"));
+ assertEquals("ab-cd-ef", StringUtils.camelToSplitName("ab-cd-ef", "-"));
+ assertEquals("abcdef", StringUtils.camelToSplitName("abcdef", "-"));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/UrlUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/UrlUtilsTest.java
new file mode 100644
index 0000000..c41a7c1
--- /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&group=test&version=1.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&group=test&version=1.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<URL> subscribed = new HashSet<URL>();
+ subscribed.add(URL.valueOf("dubbo://127.0.0.1:20880/" + service + "?group=perf&version=1.0.0"));
+ List<String> newForbid = UrlUtils.revertForbid(forbid, subscribed);
+ List<String> expectForbid = new ArrayList<String>();
+ expectForbid.add("perf/" + service + ":1.0.0");
+ assertEquals(expectForbid, newForbid);
+ }
+
+ @Test
+ public void testRevertForbid2() {
+ List<String> newForbid = UrlUtils.revertForbid(null, null);
+ assertNull(newForbid);
+ }
+
+ @Test
+ public void testRevertForbid3() {
+ String service1 = "dubbo.test.api.HelloService:1.0.0";
+ String service2 = "dubbo.test.api.HelloService:2.0.0";
+ List<String> forbid = new ArrayList<String>();
+ forbid.add(service1);
+ forbid.add(service2);
+ List<String> newForbid = UrlUtils.revertForbid(forbid, null);
+ assertEquals(forbid, newForbid);
+ }
+
+ @Test
+ public void testIsMatch() {
+ URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=test");
+ URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");
+ assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+
+ @Test
+ public void testIsMatch2() {
+ URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=2.0.0&group=test");
+ URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");
+ assertFalse(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+
+ @Test
+ public void testIsMatch3() {
+ URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=aa");
+ URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");
+ assertFalse(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+
+ @Test
+ public void testIsMatch4() {
+ URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=*");
+ URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");
+ assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+
+ @Test
+ public void testIsMatch5() {
+ URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=*&group=test");
+ URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test");
+ assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1
new file mode 100644
index 0000000..8ff8934
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1
@@ -0,0 +1,4 @@
+group=com.alibaba.dubbo.common.extensionloader.activate.impl.GroupActivateExtImpl
+value=com.alibaba.dubbo.common.extensionloader.activate.impl.ValueActivateExtImpl
+order1=com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl1
+order2=com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext1.Ext1 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext1.Ext1
new file mode 100644
index 0000000..ea1ce31
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext1.Ext1
@@ -0,0 +1,4 @@
+# Comment 1
+impl1=com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl1#Hello World
+impl2=com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl2 # Comment 2
+ impl3=com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl3 # with head space
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext2.Ext2 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext2.Ext2
new file mode 100644
index 0000000..e275f40
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext2.Ext2
@@ -0,0 +1,3 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl1
+impl2=com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl2
+impl3=com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl3
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext3.Ext3 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext3.Ext3
new file mode 100644
index 0000000..62bfa83
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext3.Ext3
@@ -0,0 +1,3 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl1
+impl2=com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl2
+impl3=com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl3
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext4.Ext4 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext4.Ext4
new file mode 100644
index 0000000..3c64e22
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext4.Ext4
@@ -0,0 +1,2 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext4.impl.Ext4Impl1
+impl2=com.alibaba.dubbo.common.extensionloader.ext4.impl.Ext4Impl2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod
new file mode 100644
index 0000000..5604232
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod
@@ -0,0 +1,4 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Impl1
+impl2=com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Impl2
+wrapper1=com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper1
+wrapper2=com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6
new file mode 100644
index 0000000..885aef5
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6
@@ -0,0 +1,2 @@
+impl1=com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl1
+impl2=com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext7.Ext7 b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext7.Ext7
new file mode 100644
index 0000000..5fa7829
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.extensionloader.ext7.Ext7
@@ -0,0 +1,2 @@
+error=com.alibaba.dubbo.common.extensionloader.ext7.impl.Ext7InitErrorImpl
+ok=com.alibaba.dubbo.common.extensionloader.ext7.impl.Ext7Impl
diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..82d8158
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1 @@
+aa=12
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1
new file mode 100644
index 0000000..2e16c11
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.activate.ActivateExt1
@@ -0,0 +1 @@
+com.alibaba.dubbo.common.extensionloader.activate.impl.ActivateExt1Impl1
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/StreamUtilsTest.txt b/dubbo-common/src/test/resources/StreamUtilsTest.txt
new file mode 100644
index 0000000..ad47100
--- /dev/null
+++ b/dubbo-common/src/test/resources/StreamUtilsTest.txt
@@ -0,0 +1 @@
+0123456789
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.fc b/dubbo-common/src/test/resources/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.fc
new file mode 100644
index 0000000..c8c083c
--- /dev/null
+++ b/dubbo-common/src/test/resources/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.fc
@@ -0,0 +1,2 @@
+a,d,e,b,c
+str3,str2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/json.flex b/dubbo-common/src/test/resources/json.flex
new file mode 100644
index 0000000..c6d4015
--- /dev/null
+++ b/dubbo-common/src/test/resources/json.flex
@@ -0,0 +1,68 @@
+package com.alibaba.dubbo.common.json;
+%%
+
+%{
+private StringBuffer sb;
+%}
+
+%table
+%unicode
+%state STR1,STR2
+
+%yylexthrow ParseException
+
+HEX = [a-fA-F0-9]
+HEX4 = {HEX}{HEX}{HEX}{HEX}
+
+IDENT = [a-zA-Z_$] [a-zA-Z0-9_$]*
+INT_LITERAL = [-]? [0-9]+
+FLOAT_LITERAL = {INT_LITERAL} ( ( \.[0-9]+ ) ? ( [eE][-+]? [0-9]+ )? )
+
+ESC1 = [^\"\\]
+ESC2 = [^\'\\]
+
+SKIP = [ \t\r\n]
+OTHERS = .
+%%
+
+<STR1>{
+ \" { yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString()); }
+ {ESC1}+ { sb.append(yytext()); }
+ \\\" { sb.append('"'); }
+}
+
+<STR2>{
+ \' { yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString()); }
+ {ESC2}+ { sb.append(yytext()); }
+ \\\' { sb.append('\''); }
+}
+
+<STR1,STR2>{
+ \\\\ { sb.append('\\'); }
+ \\\/ { sb.append('/'); }
+ \\b { sb.append('\b'); }
+ \\f { sb.append('\f'); }
+ \\n { sb.append('\n'); }
+ \\r { sb.append('\r'); }
+ \\t { sb.append('\t'); }
+ \\u{HEX4} { try{ sb.append((char)Integer.parseInt(yytext().substring(2),16)); }catch(Exception e){ throw new ParseException(e.getMessage()); } }
+}
+
+<YYINITIAL>{
+ \" { sb = new StringBuffer(); yybegin(STR1); }
+ \' { sb = new StringBuffer(); yybegin(STR2); }
+ {INT_LITERAL} { Long val = Long.valueOf(yytext()); return new JSONToken(JSONToken.INT, val); }
+ {FLOAT_LITERAL} { Double val = Double.valueOf(yytext()); return new JSONToken(JSONToken.FLOAT, val); }
+ "true"|"TRUE" { return new JSONToken(JSONToken.BOOL, Boolean.TRUE); }
+ "false"|"FALSE" { return new JSONToken(JSONToken.BOOL, Boolean.FALSE); }
+ "null"|"NULL" { return new JSONToken(JSONToken.NULL, null); }
+ {IDENT} { return new JSONToken(JSONToken.IDENT, yytext()); }
+ "{" { return new JSONToken(JSONToken.LBRACE); }
+ "}" { return new JSONToken(JSONToken.RBRACE); }
+ "[" { return new JSONToken(JSONToken.LSQUARE); }
+ "]" { return new JSONToken(JSONToken.RSQUARE); }
+ "," { return new JSONToken(JSONToken.COMMA); }
+ ":" { return new JSONToken(JSONToken.COLON); }
+ {SKIP}+ {}
+ {OTHERS} { throw new ParseException("Unexpected char [" + yytext() +"]"); }
+}
diff --git a/dubbo-common/src/test/resources/log4j.xml b/dubbo-common/src/test/resources/log4j.xml
new file mode 100644
index 0000000..a09e14d
--- /dev/null
+++ b/dubbo-common/src/test/resources/log4j.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <!-- ===================================================================== -->
+ <!-- 以下是appender的定义 -->
+ <!-- ===================================================================== -->
+ <appender name="dubbo" class="com.alibaba.dubbo.common.utils.DubboAppender">
+ <param name="encoding" value="GBK" />
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %p [%c:%M] - %m%n" />
+ </layout>
+ </appender>
+ <root>
+ <level value="INFO" />
+ <appender-ref ref="dubbo" />
+ </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/properties.load b/dubbo-common/src/test/resources/properties.load
new file mode 100644
index 0000000..21bb7f2
--- /dev/null
+++ b/dubbo-common/src/test/resources/properties.load
@@ -0,0 +1,3 @@
+a=12
+b=34
+c=56
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/pom.xml b/dubbo-config/dubbo-config-api/pom.xml
new file mode 100644
index 0000000..5c40dbc
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/pom.xml
@@ -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.
+-->
+<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-config</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-config-api</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The config api module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-monitor-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-filter-validation</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-filter-cache</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-default</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-monitor-default</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-default</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-rmi</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-injvm</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
new file mode 100644
index 0000000..4d1ba7a
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.CollectionUtils;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.config.support.Parameter;
+
+/**
+ * AbstractConfig
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractConfig implements Serializable {
+
+ private static final long serialVersionUID = 4267533505537413570L;
+
+ protected static final Logger logger = LoggerFactory.getLogger(AbstractConfig.class);
+
+ private static final int MAX_LENGTH = 100;
+
+ private static final int MAX_PATH_LENGTH = 200;
+
+ private static final Pattern PATTERN_NAME = Pattern.compile("[\\-._0-9a-zA-Z]+");
+
+ private static final Pattern PATTERN_MULTI_NAME = Pattern.compile("[,\\-._0-9a-zA-Z]+");
+
+ private static final Pattern PATTERN_METHOD_NAME = Pattern.compile("[a-zA-Z][0-9a-zA-Z]*");
+
+ private static final Pattern PATTERN_PATH = Pattern.compile("[/\\-$._0-9a-zA-Z]+");
+
+ private static final Pattern PATTERN_NAME_HAS_SYMBOL = Pattern.compile("[:*,/\\-._0-9a-zA-Z]+");
+
+ private static final Pattern PATTERN_KEY = Pattern.compile("[*,\\-._0-9a-zA-Z]+");
+
+ protected String id;
+
+ @Parameter(excluded = true)
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ private static final Map<String, String> legacyProperties = new HashMap<String, String>();
+ static {
+ legacyProperties.put("dubbo.protocol.name", "dubbo.service.protocol");
+ legacyProperties.put("dubbo.protocol.host", "dubbo.service.server.host");
+ legacyProperties.put("dubbo.protocol.port", "dubbo.service.server.port");
+ legacyProperties.put("dubbo.protocol.threads", "dubbo.service.max.thread.pool.size");
+ legacyProperties.put("dubbo.consumer.timeout", "dubbo.service.invoke.timeout");
+ legacyProperties.put("dubbo.consumer.retries", "dubbo.service.max.retry.providers");
+ legacyProperties.put("dubbo.consumer.check", "dubbo.service.allow.no.provider");
+ legacyProperties.put("dubbo.service.url", "dubbo.service.address");
+ }
+
+ private static String convertLegacyValue(String key, String value) {
+ if (value != null && value.length() > 0) {
+ if ("dubbo.service.max.retry.providers".equals(key)) {
+ return String.valueOf(Integer.parseInt(value) - 1);
+ } else if ("dubbo.service.allow.no.provider".equals(key)) {
+ return String.valueOf(! Boolean.parseBoolean(value));
+ }
+ }
+ return value;
+ }
+
+ protected void appendAnnotation(Class<?> annotationClass, Object annotation) {
+ Method[] methods = annotationClass.getMethods();
+ for (Method method : methods) {
+ if (method.getDeclaringClass() == annotation.getClass()
+ && method.getReturnType() != void.class
+ && method.getParameterTypes().length == 0
+ && Modifier.isPublic(method.getModifiers())
+ && ! Modifier.isStatic(method.getModifiers())) {
+ try {
+ String property = method.getName();
+ if ("interfaceClass".equals(property) || "interfaceName".equals(property)) {
+ property = "interface";
+ }
+ String setter = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);
+ Object value = method.invoke(annotation, new Object[0]);
+ if (value != null && ! value.equals(method.getDefaultValue())) {
+ Class<?> parameterType = ReflectUtils.getBoxedClass(method.getReturnType());
+ if ("filter".equals(property) || "listener".equals(property)) {
+ parameterType = String.class;
+ value = StringUtils.join((String[]) value, ",");
+ } else if ("parameters".equals(property)) {
+ parameterType = Map.class;
+ value = CollectionUtils.toStringMap((String[]) value);
+ }
+ try {
+ Method setterMethod = getClass().getMethod(setter, new Class<?>[] { parameterType });
+ setterMethod.invoke(this, new Object[] { value });
+ } catch (NoSuchMethodException e) {
+ // ignore
+ }
+ }
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+ }
+
+ protected static void appendProperties(AbstractConfig config) {
+ if (config == null) {
+ return;
+ }
+ String prefix = "dubbo." + getTagName(config.getClass()) + ".";
+ Method[] methods = config.getClass().getMethods();
+ for (Method method : methods) {
+ try {
+ String name = method.getName();
+ if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(method.getModifiers())
+ && method.getParameterTypes().length == 1 && isPrimitive(method.getParameterTypes()[0])) {
+ String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
+ String value = null;
+ if (config.getId() != null && config.getId().length() > 0) {
+ value = System.getProperty(prefix + config.getId() + "." + property);
+ }
+ if (value == null || value.length() == 0) {
+ value = System.getProperty(prefix + property);
+ }
+ if (value == null || value.length() == 0) {
+ Method getter;
+ try {
+ getter = config.getClass().getMethod("get" + name.substring(3), new Class<?>[0]);
+ } catch (NoSuchMethodException e) {
+ try {
+ getter = config.getClass().getMethod("is" + name.substring(3), new Class<?>[0]);
+ } catch (NoSuchMethodException e2) {
+ getter = null;
+ }
+ }
+ if (getter != null) {
+ if (getter.invoke(config, new Object[0]) == null) {
+ if (config.getId() != null && config.getId().length() > 0) {
+ value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);
+ }
+ if (value == null || value.length() == 0) {
+ value = ConfigUtils.getProperty(prefix + property);
+ }
+ if (value == null || value.length() == 0) {
+ String legacyKey = legacyProperties.get(prefix + property);
+ if (legacyKey != null && legacyKey.length() > 0) {
+ value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));
+ }
+ }
+
+ }
+ }
+ }
+ if (value != null && value.length() > 0) {
+ method.invoke(config, new Object[] {convertPrimitive(method.getParameterTypes()[0], value)});
+ }
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ private static String getTagName(Class<?> cls) {
+ String tag = cls.getSimpleName();
+ for (String suffix : SUFFIXS) {
+ if (tag.endsWith(suffix)) {
+ tag = tag.substring(0, tag.length() - suffix.length());
+ break;
+ }
+ }
+ tag = tag.toLowerCase();
+ return tag;
+ }
+
+ protected static void appendParameters(Map<String, String> parameters, Object config) {
+ appendParameters(parameters, config, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static void appendParameters(Map<String, String> parameters, Object config, String prefix) {
+ if (config == null) {
+ return;
+ }
+ Method[] methods = config.getClass().getMethods();
+ for (Method method : methods) {
+ try {
+ String name = method.getName();
+ if ((name.startsWith("get") || name.startsWith("is"))
+ && ! "getClass".equals(name)
+ && Modifier.isPublic(method.getModifiers())
+ && method.getParameterTypes().length == 0
+ && isPrimitive(method.getReturnType())) {
+ Parameter parameter = method.getAnnotation(Parameter.class);
+ if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
+ continue;
+ }
+ int i = name.startsWith("get") ? 3 : 2;
+ String prop = StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), ".");
+ String key;
+ if (parameter != null && parameter.key() != null && parameter.key().length() > 0) {
+ key = parameter.key();
+ } else {
+ key = prop;
+ }
+ Object value = method.invoke(config, new Object[0]);
+ String str = String.valueOf(value).trim();
+ if (value != null && str.length() > 0) {
+ if (parameter != null && parameter.escaped()) {
+ str = URL.encode(str);
+ }
+ if (parameter != null && parameter.append()) {
+ String pre = (String)parameters.get(Constants.DEFAULT_KEY + "." + key);
+ if (pre != null && pre.length() > 0) {
+ str = pre + "," + str;
+ }
+ pre = (String)parameters.get(key);
+ if (pre != null && pre.length() > 0) {
+ str = pre + "," + str;
+ }
+ }
+ if (prefix != null && prefix.length() > 0) {
+ key = prefix + "." + key;
+ }
+ parameters.put(key, str);
+ } else if (parameter != null && parameter.required()) {
+ throw new IllegalStateException(config.getClass().getSimpleName() + "." + key + " == null");
+ }
+ } else if ("getParameters".equals(name)
+ && Modifier.isPublic(method.getModifiers())
+ && method.getParameterTypes().length == 0
+ && method.getReturnType() == Map.class) {
+ Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);
+ if (map != null && map.size() > 0) {
+ String pre = (prefix != null && prefix.length() > 0 ? prefix + "." : "");
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ parameters.put(pre + entry.getKey().replace('-', '.'), entry.getValue());
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ }
+
+ protected static void appendAttributes(Map<Object, Object> parameters, Object config) {
+ appendAttributes(parameters, config, null);
+ }
+
+ protected static void appendAttributes(Map<Object, Object> parameters, Object config, String prefix) {
+ if (config == null) {
+ return;
+ }
+ Method[] methods = config.getClass().getMethods();
+ for (Method method : methods) {
+ try {
+ String name = method.getName();
+ if ((name.startsWith("get") || name.startsWith("is"))
+ && ! "getClass".equals(name)
+ && Modifier.isPublic(method.getModifiers())
+ && method.getParameterTypes().length == 0
+ && isPrimitive(method.getReturnType())) {
+ Parameter parameter = method.getAnnotation(Parameter.class);
+ if (parameter == null || !parameter.attribute())
+ continue;
+ String key;
+ if (parameter != null && parameter.key() != null && parameter.key().length() > 0) {
+ key = parameter.key();
+ } else {
+ int i = name.startsWith("get") ? 3 : 2;
+ key = name.substring(i, i + 1).toLowerCase() + name.substring(i + 1);
+ }
+ Object value = method.invoke(config, new Object[0]);
+ if (value != null) {
+ if (prefix != null && prefix.length() > 0) {
+ key = prefix + "." + key;
+ }
+ parameters.put(key, value);
+ }
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ }
+
+ private static boolean isPrimitive(Class<?> type) {
+ return type.isPrimitive()
+ || type == String.class
+ || type == Character.class
+ || type == Boolean.class
+ || type == Byte.class
+ || type == Short.class
+ || type == Integer.class
+ || type == Long.class
+ || type == Float.class
+ || type == Double.class
+ || type == Object.class;
+ }
+
+ private static Object convertPrimitive(Class<?> type, String value) {
+ if (type == char.class || type == Character.class) {
+ return value.length() > 0 ? value.charAt(0) : '\0';
+ } else if (type == boolean.class || type == Boolean.class) {
+ return Boolean.valueOf(value);
+ } else if (type == byte.class || type == Byte.class) {
+ return Byte.valueOf(value);
+ } else if (type == short.class || type == Short.class) {
+ return Short.valueOf(value);
+ } else if (type == int.class || type == Integer.class) {
+ return Integer.valueOf(value);
+ } else if (type == long.class || type == Long.class) {
+ return Long.valueOf(value);
+ } else if (type == float.class || type == Float.class) {
+ return Float.valueOf(value);
+ } else if (type == double.class || type == Double.class) {
+ return Double.valueOf(value);
+ }
+ return value;
+ }
+
+ protected static void checkExtension(Class<?> type, String property, String value) {
+ checkName(property, value);
+ if (value != null && value.length() > 0
+ && ! ExtensionLoader.getExtensionLoader(type).hasExtension(value)) {
+ throw new IllegalStateException("No such extension " + value + " for " + property + "/" + type.getName());
+ }
+ }
+
+ protected static void checkMultiExtension(Class<?> type, String property, String value) {
+ checkMultiName(property, value);
+ if (value != null && value.length() > 0) {
+ String[] values = value.split("\\s*[,]+\\s*");
+ for (String v : values) {
+ if (v.startsWith(Constants.REMOVE_VALUE_PREFIX)) {
+ v = v.substring(1);
+ }
+ if (Constants.DEFAULT_KEY.equals(v)) {
+ continue;
+ }
+ if (! ExtensionLoader.getExtensionLoader(type).hasExtension(v)) {
+ throw new IllegalStateException("No such extension " + v + " for " + property + "/" + type.getName());
+ }
+ }
+ }
+ }
+
+ protected static void checkLength(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, null);
+ }
+
+ protected static void checkPathLength(String property, String value) {
+ checkProperty(property, value, MAX_PATH_LENGTH, null);
+ }
+
+ protected static void checkName(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, PATTERN_NAME);
+ }
+
+ protected static void checkNameHasSymbol(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, PATTERN_NAME_HAS_SYMBOL);
+ }
+
+ protected static void checkKey(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, PATTERN_KEY);
+ }
+
+ protected static void checkMultiName(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, PATTERN_MULTI_NAME);
+ }
+
+ protected static void checkPathName(String property, String value) {
+ checkProperty(property, value, MAX_PATH_LENGTH, PATTERN_PATH);
+ }
+
+ protected static void checkMethodName(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, PATTERN_METHOD_NAME);
+ }
+
+ protected static void checkParameterName(Map<String, String> parameters) {
+ if (parameters == null || parameters.size() == 0) {
+ return;
+ }
+ for (Map.Entry<String, String> entry : parameters.entrySet()) {
+ //change by tony.chenl parameter value maybe has colon.for example napoli address
+ checkNameHasSymbol(entry.getKey(), entry.getValue());
+ }
+ }
+
+ protected static void checkProperty(String property, String value, int maxlength, Pattern pattern) {
+ if (value == null || value.length() == 0) {
+ return;
+ }
+ if(value.length() > maxlength){
+ throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" is longer than " + maxlength);
+ }
+ if (pattern != null) {
+ Matcher matcher = pattern.matcher(value);
+ if(! matcher.matches()) {
+ throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" contain illegal charactor, only digit, letter, '-', '_' and '.' is legal.");
+ }
+ }
+ }
+
+ static {
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+ public void run() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Run shutdown hook now.");
+ }
+ ProtocolConfig.destroyAll();
+ }
+ }, "DubboShutdownHook"));
+ }
+
+ private static final String[] SUFFIXS = new String[] {"Config", "Bean"};
+
+ @Override
+ public String toString() {
+ try {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<dubbo:");
+ buf.append(getTagName(getClass()));
+ Method[] methods = getClass().getMethods();
+ for (Method method : methods) {
+ try {
+ String name = method.getName();
+ if ((name.startsWith("get") || name.startsWith("is"))
+ && ! "getClass".equals(name) && ! "get".equals(name) && ! "is".equals(name)
+ && Modifier.isPublic(method.getModifiers())
+ && method.getParameterTypes().length == 0
+ && isPrimitive(method.getReturnType())) {
+ int i = name.startsWith("get") ? 3 : 2;
+ String key = name.substring(i, i + 1).toLowerCase() + name.substring(i + 1);
+ Object value = method.invoke(this, new Object[0]);
+ if (value != null) {
+ buf.append(" ");
+ buf.append(key);
+ buf.append("=\"");
+ buf.append(value);
+ buf.append("\"");
+ }
+ }
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ buf.append(" />");
+ return buf.toString();
+ } catch (Throwable t) { // 防御性容错
+ logger.warn(t.getMessage(), t);
+ return super.toString();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractInterfaceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractInterfaceConfig.java
new file mode 100644
index 0000000..5966bd3
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractInterfaceConfig.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.common.utils.UrlUtils;
+import com.alibaba.dubbo.config.support.Parameter;
+import com.alibaba.dubbo.monitor.MonitorFactory;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.InvokerListener;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.support.MockInvoker;
+
+/**
+ * AbstractDefaultConfig
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractInterfaceConfig 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 ModuleConfig module;
+
+ // 注册中心
+ protected List<RegistryConfig> registries;
+
+ // callback实例个数限制
+ private Integer callbacks;
+
+ // 连接事件
+ protected String onconnect;
+
+ // 断开事件
+ protected String ondisconnect;
+
+ // 服务暴露或引用的scope,如果为local,则表示只在当前JVM内查找.
+ private String scope;
+
+ protected void checkRegistry() {
+ // 兼容旧版本
+ if (registries == null || registries.size() == 0) {
+ String address = ConfigUtils.getProperty("dubbo.registry.address");
+ if (address != null && address.length() > 0) {
+ registries = new ArrayList<RegistryConfig>();
+ String[] as = address.split("\\s*[|]+\\s*");
+ for (String a : as) {
+ RegistryConfig registryConfig = new RegistryConfig();
+ registryConfig.setAddress(a);
+ registries.add(registryConfig);
+ }
+ }
+ }
+ if ((registries == null || registries.size() == 0)) {
+ throw new IllegalStateException((getClass().getSimpleName().startsWith("Reference")
+ ? "No such any registry to refer service in consumer "
+ : "No such any registry to export service in provider ")
+ + NetUtils.getLocalHost()
+ + " use dubbo version "
+ + Version.getVersion()
+ + ", Please add <dubbo:registry address=\"...\" /> to your spring config. If you want unregister, please set <dubbo:service registry=\"N/A\" />");
+ }
+ for (RegistryConfig registryConfig : registries) {
+ appendProperties(registryConfig);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ protected void checkApplication() {
+ // 兼容旧版本
+ if (application == null) {
+ String applicationName = ConfigUtils.getProperty("dubbo.application.name");
+ if (applicationName != null && applicationName.length() > 0) {
+ application = new ApplicationConfig();
+ }
+ }
+ if (application == null) {
+ throw new IllegalStateException(
+ "No such application config! Please add <dubbo:application name=\"...\" /> to your spring config.");
+ }
+ appendProperties(application);
+
+ String wait = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_KEY);
+ if (wait != null && wait.trim().length() > 0) {
+ System.setProperty(Constants.SHUTDOWN_WAIT_KEY, wait.trim());
+ } else {
+ wait = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_SECONDS_KEY);
+ if (wait != null && wait.trim().length() > 0) {
+ System.setProperty(Constants.SHUTDOWN_WAIT_SECONDS_KEY, wait.trim());
+ }
+ }
+ }
+
+ protected List<URL> loadRegistries(boolean provider) {
+ checkRegistry();
+ List<URL> registryList = new ArrayList<URL>();
+ if (registries != null && registries.size() > 0) {
+ for (RegistryConfig config : registries) {
+ String address = config.getAddress();
+ if (address == null || address.length() == 0) {
+ throw new IllegalStateException("registry address == null");
+ }
+ String sysaddress = System.getProperty("dubbo.registry.address");
+ if (sysaddress != null && sysaddress.length() > 0) {
+ address = sysaddress;
+ }
+ if (address != null && address.length() > 0
+ && ! RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
+ Map<String, String> map = new HashMap<String, String>();
+ appendParameters(map, application);
+ appendParameters(map, config);
+ map.put("path", RegistryService.class.getName());
+ map.put("dubbo", Version.getVersion());
+ map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
+ if (ConfigUtils.getPid() > 0) {
+ map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
+ }
+ 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);
+ if ((provider && url.getParameter(Constants.REGISTER_KEY, true))
+ || (! provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) {
+ registryList.add(url);
+ }
+ }
+ }
+ }
+ }
+ return registryList;
+ }
+
+ protected URL loadMonitor(URL registryURL) {
+ if (monitor == null) {
+ String monitorAddress = ConfigUtils.getProperty("dubbo.monitor.address");
+ String monitorProtocol = ConfigUtils.getProperty("dubbo.monitor.protocol");
+ if (monitorAddress != null && monitorAddress.length() > 0
+ || monitorProtocol != null && monitorProtocol.length() > 0) {
+ monitor = new MonitorConfig();
+ } else {
+ return null;
+ }
+ }
+ appendProperties(monitor);
+ Map<String, String> map = new HashMap<String, String>();
+ map.put(Constants.INTERFACE_KEY, MonitorService.class.getName());
+ map.put("dubbo", Version.getVersion());
+ map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
+ if (ConfigUtils.getPid() > 0) {
+ map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
+ }
+ appendParameters(map, monitor);
+ String address = monitor.getAddress();
+ String sysaddress = System.getProperty("dubbo.monitor.address");
+ if (sysaddress != null && sysaddress.length() > 0) {
+ address = sysaddress;
+ }
+ if (ConfigUtils.isNotEmpty(address)) {
+ if (! map.containsKey(Constants.PROTOCOL_KEY)) {
+ if (ExtensionLoader.getExtensionLoader(MonitorFactory.class).hasExtension("logstat")) {
+ map.put(Constants.PROTOCOL_KEY, "logstat");
+ } else {
+ map.put(Constants.PROTOCOL_KEY, "dubbo");
+ }
+ }
+ return UrlUtils.parseURL(address, map);
+ } else if (Constants.REGISTRY_PROTOCOL.equals(monitor.getProtocol()) && registryURL != null) {
+ return registryURL.setProtocol("dubbo").addParameter(Constants.PROTOCOL_KEY, "registry").addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map));
+ }
+ return null;
+ }
+
+ protected void checkInterfaceAndMethods(Class<?> interfaceClass, List<MethodConfig> methods) {
+ // 接口不能为空
+ if (interfaceClass == null) {
+ throw new IllegalStateException("interface not allow null!");
+ }
+ // 检查接口类型必需为接口
+ if(! interfaceClass.isInterface()) {
+ throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
+ }
+ // 检查方法是否在接口中存在
+ if (methods != null && methods.size() > 0) {
+ for (MethodConfig methodBean : methods) {
+ String methodName = methodBean.getName();
+ if (methodName == null || methodName.length() == 0) {
+ throw new IllegalStateException("<dubbo:method> name attribute is required! Please check: <dubbo:service interface=\"" + interfaceClass.getName() + "\" ... ><dubbo:method name=\"\" ... /></<dubbo:reference>");
+ }
+ boolean hasMethod = false;
+ for (java.lang.reflect.Method method : interfaceClass.getMethods()) {
+ if (method.getName().equals(methodName)) {
+ hasMethod = true;
+ break;
+ }
+ }
+ if (!hasMethod) {
+ throw new IllegalStateException("The interface " + interfaceClass.getName()
+ + " not found method " + methodName);
+ }
+ }
+ }
+ }
+
+ protected void checkStubAndMock(Class<?> interfaceClass) {
+ if (ConfigUtils.isNotEmpty(local)) {
+ Class<?> localClass = ConfigUtils.isDefault(local) ? ReflectUtils.forName(interfaceClass.getName() + "Local") : ReflectUtils.forName(local);
+ if (! interfaceClass.isAssignableFrom(localClass)) {
+ throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceClass.getName());
+ }
+ try {
+ ReflectUtils.findConstructor(localClass, interfaceClass);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() + "(" + interfaceClass.getName() + ")\" in local implemention class " + localClass.getName());
+ }
+ }
+ if (ConfigUtils.isNotEmpty(stub)) {
+ Class<?> localClass = ConfigUtils.isDefault(stub) ? ReflectUtils.forName(interfaceClass.getName() + "Stub") : ReflectUtils.forName(stub);
+ if (! interfaceClass.isAssignableFrom(localClass)) {
+ throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceClass.getName());
+ }
+ try {
+ ReflectUtils.findConstructor(localClass, interfaceClass);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() + "(" + interfaceClass.getName() + ")\" in local implemention class " + localClass.getName());
+ }
+ }
+ if (ConfigUtils.isNotEmpty(mock)) {
+ if (mock.startsWith(Constants.RETURN_PREFIX)) {
+ String value = mock.substring(Constants.RETURN_PREFIX.length());
+ try {
+ MockInvoker.parseMockValue(value);
+ } catch (Exception e) {
+ throw new IllegalStateException("Illegal mock json value in <dubbo:service ... mock=\"" + mock + "\" />");
+ }
+ } else {
+ Class<?> mockClass = ConfigUtils.isDefault(mock) ? ReflectUtils.forName(interfaceClass.getName() + "Mock") : ReflectUtils.forName(mock);
+ if (! interfaceClass.isAssignableFrom(mockClass)) {
+ throw new IllegalStateException("The mock implemention class " + mockClass.getName() + " not implement interface " + interfaceClass.getName());
+ }
+ try {
+ mockClass.getConstructor(new Class<?>[0]);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName());
+ }
+ }
+ }
+ }
+
+ /**
+ * @deprecated Replace to <code>getStub()</code>
+ * @return local
+ */
+ @Deprecated
+ public String getLocal() {
+ return local;
+ }
+
+ /**
+ * @deprecated Replace to <code>setStub(String)</code>
+ * @param local
+ */
+ @Deprecated
+ public void setLocal(String local) {
+ checkName("local", local);
+ this.local = local;
+ }
+
+ /**
+ * @deprecated Replace to <code>setStub(Boolean)</code>
+ * @param local
+ */
+ @Deprecated
+ public void setLocal(Boolean local) {
+ if (local == null) {
+ setLocal((String) null);
+ } else {
+ setLocal(String.valueOf(local));
+ }
+ }
+
+ public String getStub() {
+ return stub;
+ }
+
+ public void setStub(String stub) {
+ checkName("stub", stub);
+ this.stub = stub;
+ }
+
+ public void setStub(Boolean stub) {
+ if (local == null) {
+ setStub((String) null);
+ } else {
+ setStub(String.valueOf(stub));
+ }
+ }
+
+ public String getCluster() {
+ return cluster;
+ }
+
+ public void setCluster(String cluster) {
+ checkExtension(Cluster.class, "cluster", cluster);
+ this.cluster = cluster;
+ }
+
+ public String getProxy() {
+ return proxy;
+ }
+
+ public void setProxy(String proxy) {
+ checkExtension(ProxyFactory.class, "proxy", proxy);
+ this.proxy = proxy;
+ }
+
+ public Integer getConnections() {
+ return connections;
+ }
+
+ public void setConnections(Integer connections) {
+ this.connections = connections;
+ }
+
+ @Parameter(key = Constants.REFERENCE_FILTER_KEY, append = true)
+ public String getFilter() {
+ return filter;
+ }
+
+ public void setFilter(String filter) {
+ checkMultiExtension(Filter.class, "filter", filter);
+ this.filter = filter;
+ }
+
+ @Parameter(key = Constants.INVOKER_LISTENER_KEY, append = true)
+ public String getListener() {
+ checkMultiExtension(InvokerListener.class, "listener", listener);
+ return listener;
+ }
+
+ public void setListener(String listener) {
+ this.listener = listener;
+ }
+
+ public String getLayer() {
+ return layer;
+ }
+
+ public void setLayer(String layer) {
+ checkNameHasSymbol("layer", layer);
+ this.layer = layer;
+ }
+
+ public ApplicationConfig getApplication() {
+ return application;
+ }
+
+ public void setApplication(ApplicationConfig application) {
+ this.application = application;
+ }
+
+ public ModuleConfig getModule() {
+ return module;
+ }
+
+ public void setModule(ModuleConfig module) {
+ this.module = module;
+ }
+
+ 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;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java
new file mode 100644
index 0000000..e9bc704
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.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.config;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.config.support.Parameter;
+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 String merger;
+
+ // 服务接口的失败mock实现类名
+ protected String cache;
+
+ // 服务接口的失败mock实现类名
+ protected String validation;
+
+ // 自定义参数
+ protected Map<String, String> parameters;
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public Integer getRetries() {
+ return retries;
+ }
+
+ public void setRetries(Integer retries) {
+ this.retries = retries;
+ }
+
+ public String getLoadbalance() {
+ return loadbalance;
+ }
+
+ public void setLoadbalance(String loadbalance) {
+ checkExtension(LoadBalance.class, "loadbalance", loadbalance);
+ this.loadbalance = loadbalance;
+ }
+
+ public Boolean isAsync() {
+ return async;
+ }
+
+ public void setAsync(Boolean async) {
+ this.async = async;
+ }
+
+ public Integer getActives() {
+ return actives;
+ }
+
+ public void setActives(Integer actives) {
+ this.actives = actives;
+ }
+
+ public Boolean getSent() {
+ return sent;
+ }
+
+ public void setSent(Boolean sent) {
+ this.sent = sent;
+ }
+
+ @Parameter(escaped = true)
+ public String getMock() {
+ return mock;
+ }
+
+ public void setMock(String mock) {
+ if (mock != null && mock.startsWith(Constants.RETURN_PREFIX)) {
+ checkLength("mock", mock);
+ } else {
+ checkName("mock", mock);
+ }
+ this.mock = mock;
+ }
+
+ public void setMock(Boolean mock) {
+ if (mock == null) {
+ setMock((String) null);
+ } else {
+ setMock(String.valueOf(mock));
+ }
+ }
+
+ public String getMerger() {
+ return merger;
+ }
+
+ public void setMerger(String merger) {
+ this.merger = merger;
+ }
+
+ public String getCache() {
+ return cache;
+ }
+
+ public void setCache(String cache) {
+ this.cache = cache;
+ }
+
+ public String getValidation() {
+ return validation;
+ }
+
+ public void setValidation(String validation) {
+ this.validation = validation;
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Map<String, String> parameters) {
+ checkParameterName(parameters);
+ this.parameters = parameters;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.java
new file mode 100644
index 0000000..6ac68d0
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.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.config;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.config.support.Parameter;
+import com.alibaba.dubbo.rpc.InvokerListener;
+
+
+/**
+ * AbstractConsumerConfig
+ *
+ * @see com.alibaba.dubbo.config.ReferenceConfig
+ * @author william.liangf
+ */
+public abstract class AbstractReferenceConfig extends AbstractInterfaceConfig {
+
+ private static final long serialVersionUID = -2786526984373031126L;
+
+ // ======== 引用缺省值,当引用属性未设置时使用该缺省值替代 ========
+
+ // 检查服务提供者是否存在
+ protected Boolean check;
+
+ // 是否加载时即刻初始化
+ protected Boolean init;
+
+ // 是否使用泛接口
+ protected Boolean generic;
+
+ // 优先从JVM内获取引用实例
+ protected Boolean injvm;
+
+ // lazy create connection
+ protected Boolean lazy;
+
+ protected String reconnect;
+
+ protected Boolean sticky;
+
+ //stub是否支持event事件. //TODO slove merge problem
+ protected Boolean stubevent ;//= Constants.DEFAULT_STUB_EVENT;
+
+ // 版本
+ protected String version;
+
+ // 服务分组
+ protected String group;
+
+ 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;
+ }
+
+ /**
+ * @return
+ * @deprecated 通过scope进行判断,scope=local
+ */
+ @Deprecated
+ public Boolean isInjvm() {
+ return injvm;
+ }
+
+ /**
+ * @param injvm
+ * @deprecated 通过scope设置,scope=local表示使用injvm协议.
+ */
+ @Deprecated
+ public void setInjvm(Boolean injvm) {
+ this.injvm = injvm;
+ }
+
+ @Parameter(key = Constants.REFERENCE_FILTER_KEY, append = true)
+ public String getFilter() {
+ return super.getFilter();
+ }
+
+ @Parameter(key = Constants.INVOKER_LISTENER_KEY, append = true)
+ public String getListener() {
+ return super.getListener();
+ }
+
+ @Override
+ public void setListener(String listener) {
+ checkMultiExtension(InvokerListener.class, "listener", listener);
+ super.setListener(listener);
+ }
+
+ @Parameter(key = Constants.LAZY_CONNECT_KEY)
+ public Boolean getLazy() {
+ return lazy;
+ }
+
+ public void setLazy(Boolean lazy) {
+ this.lazy = lazy;
+ }
+
+ @Override
+ public void setOnconnect(String onconnect) {
+ if (onconnect != null && onconnect.length() >0){
+ this.stubevent = true;
+ }
+ super.setOnconnect(onconnect);
+ }
+
+ @Override
+ public void setOndisconnect(String ondisconnect) {
+ if (ondisconnect != null && ondisconnect.length() >0){
+ this.stubevent = true;
+ }
+ super.setOndisconnect(ondisconnect);
+ }
+
+ @Parameter(key = Constants.STUB_EVENT_KEY)
+ public Boolean getStubevent() {
+ return stubevent;
+ }
+
+ @Parameter(key = Constants.RECONNECT_KEY)
+ public String getReconnect() {
+ return reconnect;
+ }
+
+ public void setReconnect(String reconnect) {
+ this.reconnect = reconnect;
+ }
+
+ @Parameter(key = Constants.CLUSTER_STICKY_KEY)
+ public Boolean getSticky() {
+ return sticky;
+ }
+
+ public void setSticky(Boolean sticky) {
+ this.sticky = sticky;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ checkKey("version", version);
+ this.version = version;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ checkKey("group", group);
+ this.group = group;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java
new file mode 100644
index 0000000..39cf8e7
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.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.config;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.config.support.Parameter;
+import com.alibaba.dubbo.rpc.ExporterListener;
+
+/**
+ * AbstractServiceConfig
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractServiceConfig extends AbstractInterfaceConfig {
+
+ private static final long serialVersionUID = 1L;
+
+ // 服务版本
+ protected String version;
+
+ // 服务分组
+ protected String group;
+
+ // 服务是否已经deprecated
+ protected Boolean deprecated;
+
+ // 延迟暴露
+ protected Integer delay;
+
+ // 是否暴露
+ protected Boolean export;
+
+ // 权重
+ protected Integer weight;
+
+ // 应用文档
+ protected String document;
+
+ // 在注册中心上注册成动态的还是静态的服务
+ protected Boolean dynamic;
+
+ // 是否使用令牌
+ protected String token;
+
+ // 访问日志
+ protected String accesslog;
+
+ // 允许执行请求数
+ private Integer executes;
+
+ protected List<ProtocolConfig> protocols;
+
+ // 是否注册
+ private Boolean register;
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ checkKey("version", version);
+ this.version = version;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ checkKey("group", group);
+ this.group = group;
+ }
+
+ public Integer getDelay() {
+ return delay;
+ }
+
+ public void setDelay(Integer delay) {
+ this.delay = delay;
+ }
+
+ public Boolean getExport() {
+ return export;
+ }
+
+ public void setExport(Boolean export) {
+ this.export = export;
+ }
+
+ 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);
+ }
+
+ public Boolean isRegister() {
+ return register;
+ }
+
+ public void setRegister(Boolean register) {
+ this.register = register;
+ if (Boolean.FALSE.equals(register)) {
+ setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.java
new file mode 100644
index 0000000..25d05e1
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.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.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.support.Parameter;
+
+
+/**
+ * ApplicationConfig
+ *
+ * @author william.liangf
+ */
+public class ApplicationConfig extends AbstractConfig {
+
+ private static final long serialVersionUID = 5508512956753757169L;
+
+ // 应用名称
+ private String name;
+
+ // 模块版本
+ private String version;
+
+ // 应用负责人
+ private String owner;
+
+ // 组织名(BU或部门)
+ private String organization;
+
+ // 分层
+ private String architecture;
+
+ // 环境,如:dev/test/run
+ private String environment;
+
+ // Java代码编译器
+ private String compiler;
+
+ // 日志输出方式
+ private String logger;
+
+ // 注册中心
+ private List<RegistryConfig> registries;
+
+ // 服务监控
+ private MonitorConfig monitor;
+
+ // 是否为缺省
+ private Boolean isDefault;
+
+ public ApplicationConfig() {
+ }
+
+ public ApplicationConfig(String name) {
+ setName(name);
+ }
+
+ @Parameter(key = Constants.APPLICATION_KEY, required = true)
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ checkName("name", name);
+ this.name = name;
+ if (id == null || id.length() == 0) {
+ id = name;
+ }
+ }
+
+ @Parameter(key = "application.version")
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public void setOwner(String owner) {
+ checkName("owner", owner);
+ this.owner = owner;
+ }
+
+ public String getOrganization() {
+ return organization;
+ }
+
+ public void setOrganization(String organization) {
+ checkName("organization", organization);
+ this.organization = organization;
+ }
+
+ public String getArchitecture() {
+ return architecture;
+ }
+
+ public void setArchitecture(String architecture) {
+ checkName("architecture", architecture);
+ this.architecture = architecture;
+ }
+
+ public String getEnvironment() {
+ return environment;
+ }
+
+ public void setEnvironment(String environment) {
+ checkName("environment", environment);
+ if(environment != null) {
+ if (! ("develop".equals(environment) || "test".equals(environment) || "product".equals(environment))) {
+ throw new IllegalStateException("Unsupported environment: " + environment + ", only support develop/test/product, default is product.");
+ }
+ }
+ this.environment = environment;
+ }
+
+ public RegistryConfig getRegistry() {
+ return registries == null || registries.size() == 0 ? null : registries.get(0);
+ }
+
+ public void setRegistry(RegistryConfig registry) {
+ List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);
+ registries.add(registry);
+ this.registries = registries;
+ }
+
+ public List<RegistryConfig> getRegistries() {
+ return registries;
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ public void setRegistries(List<? extends RegistryConfig> registries) {
+ this.registries = (List<RegistryConfig>)registries;
+ }
+
+ public MonitorConfig getMonitor() {
+ return monitor;
+ }
+
+ public void setMonitor(MonitorConfig monitor) {
+ this.monitor = monitor;
+ }
+
+ public void setMonitor(String monitor) {
+ this.monitor = new MonitorConfig(monitor);
+ }
+
+ public String getCompiler() {
+ return compiler;
+ }
+
+ public void setCompiler(String compiler) {
+ this.compiler = compiler;
+ AdaptiveCompiler.setDefaultCompiler(compiler);
+ }
+
+ public String getLogger() {
+ return logger;
+ }
+
+ public void setLogger(String logger) {
+ this.logger = logger;
+ LoggerFactory.setLoggerAdapter(logger);
+ }
+
+ public Boolean isDefault() {
+ return isDefault;
+ }
+
+ public void setDefault(Boolean isDefault) {
+ this.isDefault = isDefault;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.java
new file mode 100644
index 0000000..72ab60b
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.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.config;
+
+import java.io.Serializable;
+
+import com.alibaba.dubbo.config.support.Parameter;
+
+/**
+ * @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/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.java
new file mode 100644
index 0000000..5de674c
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+/**
+ * ConsumerConfig
+ *
+ * @author william.liangf
+ */
+public class ConsumerConfig extends AbstractReferenceConfig {
+
+ private static final long serialVersionUID = 2827274711143680600L;
+
+ // 是否为缺省
+ private Boolean isDefault;
+
+ @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));
+ }
+ }
+
+ public Boolean isDefault() {
+ return isDefault;
+ }
+
+ public void setDefault(Boolean isDefault) {
+ this.isDefault = isDefault;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/MethodConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/MethodConfig.java
new file mode 100644
index 0000000..b1065e4
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/MethodConfig.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.config.support.Parameter;
+
+/**
+ * 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;
+ if (id == null || id.length() == 0) {
+ id = name;
+ }
+ }
+
+ public Integer getStat() {
+ return stat;
+ }
+
+ @Deprecated
+ public void setStat(Integer stat) {
+ this.stat = stat;
+ }
+
+ @Deprecated
+ public Boolean isRetry() {
+ return retry;
+ }
+
+ @Deprecated
+ public void setRetry(Boolean retry) {
+ this.retry = retry;
+ }
+
+ @Deprecated
+ public Boolean isReliable() {
+ return reliable;
+ }
+
+ @Deprecated
+ public void setReliable(Boolean reliable) {
+ this.reliable = reliable;
+ }
+
+ public Integer getExecutes() {
+ return executes;
+ }
+
+ public void setExecutes(Integer executes) {
+ this.executes = executes;
+ }
+
+ public Boolean getDeprecated() {
+ return deprecated;
+ }
+
+ public void setDeprecated(Boolean deprecated) {
+ this.deprecated = deprecated;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void setArguments(List<? extends ArgumentConfig> arguments) {
+ this.arguments = (List<ArgumentConfig>) arguments;
+ }
+
+ public List<ArgumentConfig> getArguments() {
+ return arguments;
+ }
+
+ public Boolean getSticky() {
+ return sticky;
+ }
+
+ public void setSticky(Boolean sticky) {
+ this.sticky = sticky;
+ }
+
+ @Parameter(key = Constants.ON_RETURN_INSTANCE_KEY, excluded = true, attribute = true)
+ public Object getOnreturn() {
+ return onreturn;
+ }
+
+ public void setOnreturn(Object onreturn) {
+ this.onreturn = onreturn;
+ }
+
+ @Parameter(key = Constants.ON_RETURN_METHOD_KEY, excluded = true, attribute = true)
+ public String getOnreturnMethod() {
+ return onreturnMethod;
+ }
+
+ public void setOnreturnMethod(String onreturnMethod) {
+ this.onreturnMethod = onreturnMethod;
+ }
+
+ @Parameter(key = Constants.ON_THROW_INSTANCE_KEY, excluded = true, attribute = true)
+ public Object getOnthrow() {
+ return onthrow;
+ }
+
+ public void setOnthrow(Object onthrow) {
+ this.onthrow = onthrow;
+ }
+
+ @Parameter(key = Constants.ON_THROW_METHOD_KEY, excluded = true, attribute = true)
+ public String getOnthrowMethod() {
+ return onthrowMethod;
+ }
+
+ public void setOnthrowMethod(String onthrowMethod) {
+ this.onthrowMethod = onthrowMethod;
+ }
+
+ @Parameter(key = Constants.ON_INVOKE_INSTANCE_KEY, excluded = true, attribute = true)
+ public Object getOninvoke() {
+ return oninvoke;
+ }
+
+ public void setOninvoke(Object oninvoke) {
+ this.oninvoke = oninvoke;
+ }
+
+ @Parameter(key = Constants.ON_INVOKE_METHOD_KEY, excluded = true, attribute = true)
+ public String getOninvokeMethod() {
+ return oninvokeMethod;
+ }
+
+ public void setOninvokeMethod(String oninvokeMethod) {
+ this.oninvokeMethod = oninvokeMethod;
+ }
+
+ public Boolean isReturn() {
+ return isReturn;
+ }
+
+ public void setReturn(Boolean isReturn) {
+ this.isReturn = isReturn;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ModuleConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ModuleConfig.java
new file mode 100644
index 0000000..98d34e4
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ModuleConfig.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.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.config.support.Parameter;
+
+/**
+ * ModuleConfig
+ *
+ * @author william.liangf
+ */
+public class ModuleConfig extends AbstractConfig {
+
+ private static final long serialVersionUID = 5508512956753757169L;
+
+ // 模块名称
+ private String name;
+
+ // 模块版本
+ private String version;
+
+ // 应用负责人
+ private String owner;
+
+ // 组织名(BU或部门)
+ private String organization;
+
+ // 注册中心
+ private List<RegistryConfig> registries;
+
+ // 服务监控
+ private MonitorConfig monitor;
+
+ // 是否为缺省
+ private Boolean isDefault;
+
+ public ModuleConfig() {
+ }
+
+ public ModuleConfig(String name) {
+ setName(name);
+ }
+
+ @Parameter(key = "module", required = true)
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ checkName("name", name);
+ this.name = name;
+ if (id == null || id.length() == 0) {
+ id = name;
+ }
+ }
+
+ @Parameter(key = "module.version")
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ 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 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 Boolean isDefault() {
+ return isDefault;
+ }
+
+ public void setDefault(Boolean isDefault) {
+ this.isDefault = isDefault;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/MonitorConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/MonitorConfig.java
new file mode 100644
index 0000000..8ddd32b
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/MonitorConfig.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;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.config.support.Parameter;
+
+/**
+ * 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;
+
+ // 是否为缺省
+ private Boolean isDefault;
+
+ 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;
+ }
+
+ public Boolean isDefault() {
+ return isDefault;
+ }
+
+ public void setDefault(Boolean isDefault) {
+ this.isDefault = isDefault;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
new file mode 100644
index 0000000..8e2e744
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.serialize.Serialization;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.common.threadpool.ThreadPool;
+import com.alibaba.dubbo.config.support.Parameter;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+import com.alibaba.dubbo.remoting.Dispather;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.Transporter;
+import com.alibaba.dubbo.remoting.exchange.Exchanger;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.rpc.Protocol;
+
+/**
+ * ProtocolConfig
+ *
+ * @author william.liangf
+ */
+public class ProtocolConfig extends AbstractConfig {
+
+ private static final long serialVersionUID = 6913423882496634749L;
+
+ // 服务协议
+ private String name;
+
+ // 服务IP地址(多网卡时使用)
+ private String host;
+
+ // 服务端口
+ private Integer port;
+
+ // 上下文路径
+ private String contextpath;
+
+ // 线程池类型
+ private String threadpool;
+
+ // 线程池大小(固定大小)
+ private Integer threads;
+
+ // IO线程池大小(固定大小)
+ private Integer iothreads;
+
+ // 线程池队列大小
+ private Integer queues;
+
+ // 最大接收连接数
+ private Integer accepts;
+
+ // 协议编码
+ private String codec;
+
+ // 序列化方式
+ private String serialization;
+
+ // 字符集
+ private String charset;
+
+ // 最大请求数据长度
+ private Integer payload;
+
+ // 缓存区大小
+ private Integer buffer;
+
+ // 心跳间隔
+ private Integer heartbeat;
+
+ // 访问日志
+ private String accesslog;
+
+ // 网络传输方式
+ private String transporter;
+
+ // 信息交换方式
+ private String exchanger;
+
+ // 信息线程模型派发方式
+ private String dispather;
+
+ // 对称网络组网方式
+ private String networker;
+
+ // 服务器端实现
+ private String server;
+
+ // 客户端实现
+ private String client;
+
+ // 支持的telnet命令,多个命令用逗号分隔
+ private String telnet;
+
+ // 命令行提示符
+ private String prompt;
+
+ // status检查
+ private String status;
+
+ // 是否注册
+ private Boolean register;
+
+ // 参数
+ private Map<String, String> parameters;
+
+ // 是否为缺省
+ private Boolean isDefault;
+
+ public ProtocolConfig() {
+ }
+
+ public ProtocolConfig(String name) {
+ setName(name);
+ }
+
+ public ProtocolConfig(String name, int port) {
+ setName(name);
+ setPort(port);
+ }
+
+ @Parameter(excluded = true)
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ checkName("name", name);
+ this.name = name;
+ if (id == null || id.length() == 0) {
+ id = name;
+ }
+ }
+
+ @Parameter(excluded = true)
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ checkName("host", host);
+ this.host = host;
+ }
+
+ @Parameter(excluded = true)
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ @Deprecated
+ @Parameter(excluded = true)
+ public String getPath() {
+ return getContextpath();
+ }
+
+ @Deprecated
+ public void setPath(String path) {
+ setContextpath(path);
+ }
+
+ @Parameter(excluded = true)
+ public String getContextpath() {
+ return contextpath;
+ }
+
+ public void setContextpath(String contextpath) {
+ checkPathName("contextpath", contextpath);
+ this.contextpath = contextpath;
+ }
+
+ public String getThreadpool() {
+ return threadpool;
+ }
+
+ public void setThreadpool(String threadpool) {
+ checkExtension(ThreadPool.class, "threadpool", threadpool);
+ this.threadpool = threadpool;
+ }
+
+ public Integer getThreads() {
+ return threads;
+ }
+
+ public void setThreads(Integer threads) {
+ this.threads = threads;
+ }
+
+ public Integer getIothreads() {
+ return iothreads;
+ }
+
+ public void setIothreads(Integer iothreads) {
+ this.iothreads = iothreads;
+ }
+
+ public Integer getQueues() {
+ return queues;
+ }
+
+ public void setQueues(Integer queues) {
+ this.queues = queues;
+ }
+
+ public Integer getAccepts() {
+ return accepts;
+ }
+
+ public void setAccepts(Integer accepts) {
+ this.accepts = accepts;
+ }
+
+ public String getCodec() {
+ return codec;
+ }
+
+ public void setCodec(String codec) {
+ if ("dubbo".equals(name)) {
+ checkMultiExtension(Codec.class, "codec", codec);
+ }
+ this.codec = codec;
+ }
+
+ public String getSerialization() {
+ return serialization;
+ }
+
+ public void setSerialization(String serialization) {
+ if ("dubbo".equals(name)) {
+ checkMultiExtension(Serialization.class, "serialization", serialization);
+ }
+ this.serialization = serialization;
+ }
+
+ public String getCharset() {
+ return charset;
+ }
+
+ public void setCharset(String charset) {
+ this.charset = charset;
+ }
+
+ public Integer getPayload() {
+ return payload;
+ }
+
+ public void setPayload(Integer payload) {
+ this.payload = payload;
+ }
+
+ public Integer getBuffer() {
+ return buffer;
+ }
+
+ public void setBuffer(Integer buffer) {
+ this.buffer = buffer;
+ }
+
+ public Integer getHeartbeat() {
+ return heartbeat;
+ }
+
+ public void setHeartbeat(Integer heartbeat) {
+ this.heartbeat = heartbeat;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public void setServer(String server) {
+ if ("dubbo".equals(name)) {
+ checkMultiExtension(Transporter.class, "server", server);
+ }
+ this.server = server;
+ }
+
+ public String getClient() {
+ return client;
+ }
+
+ public void setClient(String client) {
+ if ("dubbo".equals(name)) {
+ checkMultiExtension(Transporter.class, "client", client);
+ }
+ this.client = client;
+ }
+
+ public String getAccesslog() {
+ return accesslog;
+ }
+
+ public void setAccesslog(String accesslog) {
+ this.accesslog = accesslog;
+ }
+
+ public String getTelnet() {
+ return telnet;
+ }
+
+ public void setTelnet(String telnet) {
+ checkMultiExtension(TelnetHandler.class, "telnet", telnet);
+ this.telnet = telnet;
+ }
+
+ @Parameter(escaped = true)
+ public String getPrompt() {
+ return prompt;
+ }
+
+ public void setPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ checkMultiExtension(StatusChecker.class, "status", status);
+ this.status = status;
+ }
+
+ public Boolean isRegister() {
+ return register;
+ }
+
+ public void setRegister(Boolean register) {
+ this.register = register;
+ }
+
+ public String getTransporter() {
+ return transporter;
+ }
+
+ public void setTransporter(String transporter) {
+ checkExtension(Transporter.class, "transporter", transporter);
+ this.transporter = transporter;
+ }
+
+ public String getExchanger() {
+ return exchanger;
+ }
+
+ public void setExchanger(String exchanger) {
+ checkExtension(Exchanger.class, "exchanger", exchanger);
+ this.exchanger = exchanger;
+ }
+
+ public String getDispather() {
+ return dispather;
+ }
+
+ public void setDispather(String dispather) {
+ checkExtension(Dispather.class, "dispather", exchanger);
+ this.dispather = dispather;
+ }
+
+ public String getNetworker() {
+ return networker;
+ }
+
+ public void setNetworker(String networker) {
+ this.networker = networker;
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Map<String, String> parameters) {
+ this.parameters = parameters;
+ }
+
+ public Boolean isDefault() {
+ return isDefault;
+ }
+
+ public void setDefault(Boolean isDefault) {
+ this.isDefault = isDefault;
+ }
+
+ public void destory() {
+ if (name != null) {
+ ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).destroy();;
+ }
+ }
+
+ public static void destroyAll() {
+ AbstractRegistryFactory.destroyAll();
+ ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
+ for (String protocolName : loader.getLoadedExtensions()) {
+ try {
+ Protocol protocol = loader.getLoadedExtension(protocolName);
+ if (protocol != null) {
+ protocol.destroy();
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java
new file mode 100644
index 0000000..de257d5
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.config.support.Parameter;
+import com.alibaba.dubbo.remoting.Dispather;
+import com.alibaba.dubbo.remoting.Transporter;
+import com.alibaba.dubbo.remoting.exchange.Exchanger;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+
+/**
+ * ProviderConfig
+ *
+ * @see com.alibaba.dubbo.config.ProtocolConfig
+ * @see com.alibaba.dubbo.config.ServiceConfig
+ * @author william.liangf
+ */
+public class ProviderConfig extends AbstractServiceConfig {
+
+ private static final long serialVersionUID = 6913423882496634749L;
+
+ // ======== 协议缺省值,当协议属性未设置时使用该缺省值替代 ========
+
+ // 服务IP地址(多网卡时使用)
+ private String host;
+
+ // 服务端口
+ private Integer port;
+
+ // 上下
+ private String contextpath;
+
+ // 线程池类型
+ private String threadpool;
+
+ // 线程池大小(固定大小)
+ private Integer threads;
+
+ // IO线程池大小(固定大小)
+ private Integer iothreads;
+
+ // 线程池队列大小
+ private Integer queues;
+
+ // 最大接收连接数
+ private Integer accepts;
+
+ // 协议编码
+ private String codec;
+
+ // 序列化方式
+ private String serialization;
+
+ // 字符集
+ private String charset;
+
+ // 最大请求数据长度
+ private Integer payload;
+
+ // 缓存区大小
+ private Integer buffer;
+
+ // 网络传输方式
+ private String transporter;
+
+ // 信息交换方式
+ private String exchanger;
+
+ // 信息线程模型派发方式
+ private String dispather;
+
+ // 对称网络组网方式
+ private String networker;
+
+ // 服务器端实现
+ private String server;
+
+ // 客户端实现
+ private String client;
+
+ // 支持的telnet命令,多个命令用逗号分隔
+ private String telnet;
+
+ // 命令行提示符
+ private String prompt;
+
+ // status检查
+ private String status;
+
+ // 停止时等候时间
+ private Integer wait;
+
+ // 是否为缺省
+ private Boolean isDefault;
+
+ @Deprecated
+ public void setProtocol(String protocol) {
+ this.protocols = Arrays.asList(new ProtocolConfig[] {new ProtocolConfig(protocol)});
+ }
+
+ @Parameter(excluded = true)
+ public Boolean isDefault() {
+ return isDefault;
+ }
+
+ @Deprecated
+ public void setDefault(Boolean isDefault) {
+ this.isDefault = isDefault;
+ }
+
+ @Parameter(excluded = true)
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ @Parameter(excluded = true)
+ public Integer getPort() {
+ return port;
+ }
+
+ @Deprecated
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ @Deprecated
+ @Parameter(excluded = true)
+ public String getPath() {
+ return getContextpath();
+ }
+
+ @Deprecated
+ public void setPath(String path) {
+ setContextpath(path);
+ }
+
+ @Parameter(excluded = true)
+ public String getContextpath() {
+ return contextpath;
+ }
+
+ public void setContextpath(String contextpath) {
+ checkPathName("contextpath", contextpath);
+ this.contextpath = contextpath;
+ }
+
+ public String getThreadpool() {
+ return threadpool;
+ }
+
+ public void setThreadpool(String threadpool) {
+ checkExtension(ThreadPool.class, "threadpool", threadpool);
+ this.threadpool = threadpool;
+ }
+
+ public Integer getThreads() {
+ return threads;
+ }
+
+ public void setThreads(Integer threads) {
+ this.threads = threads;
+ }
+
+ public Integer getIothreads() {
+ return iothreads;
+ }
+
+ public void setIothreads(Integer iothreads) {
+ this.iothreads = iothreads;
+ }
+
+ public Integer getQueues() {
+ return queues;
+ }
+
+ public void setQueues(Integer queues) {
+ this.queues = queues;
+ }
+
+ public Integer getAccepts() {
+ return accepts;
+ }
+
+ public void setAccepts(Integer accepts) {
+ this.accepts = accepts;
+ }
+
+ public String getCodec() {
+ return codec;
+ }
+
+ public void setCodec(String codec) {
+ this.codec = codec;
+ }
+
+ public String getSerialization() {
+ return serialization;
+ }
+
+ public void setSerialization(String serialization) {
+ this.serialization = serialization;
+ }
+
+ public String getCharset() {
+ return charset;
+ }
+
+ public void setCharset(String charset) {
+ this.charset = charset;
+ }
+
+ public Integer getPayload() {
+ return payload;
+ }
+
+ public void setPayload(Integer payload) {
+ this.payload = payload;
+ }
+
+ public Integer getBuffer() {
+ return buffer;
+ }
+
+ public void setBuffer(Integer buffer) {
+ this.buffer = buffer;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ public String getClient() {
+ return client;
+ }
+
+ public void setClient(String client) {
+ this.client = client;
+ }
+
+ public String getTelnet() {
+ return telnet;
+ }
+
+ public void setTelnet(String telnet) {
+ checkMultiExtension(TelnetHandler.class, "telnet", telnet);
+ this.telnet = telnet;
+ }
+
+ @Parameter(escaped = true)
+ public String getPrompt() {
+ return prompt;
+ }
+
+ public void setPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ checkMultiExtension(StatusChecker.class, "status", status);
+ this.status = status;
+ }
+
+ @Override
+ public String getCluster() {
+ return super.getCluster();
+ }
+
+ @Override
+ public Integer getConnections() {
+ return super.getConnections();
+ }
+
+ @Override
+ public Integer getTimeout() {
+ return super.getTimeout();
+ }
+
+ @Override
+ public Integer getRetries() {
+ return super.getRetries();
+ }
+
+ @Override
+ public String getLoadbalance() {
+ return super.getLoadbalance();
+ }
+
+ @Override
+ public Boolean isAsync() {
+ return super.isAsync();
+ }
+
+ @Override
+ public Integer getActives() {
+ return super.getActives();
+ }
+
+ public String getTransporter() {
+ return transporter;
+ }
+
+ public void setTransporter(String transporter) {
+ checkExtension(Transporter.class, "transporter", transporter);
+ this.transporter = transporter;
+ }
+
+ public String getExchanger() {
+ return exchanger;
+ }
+
+ public void setExchanger(String exchanger) {
+ checkExtension(Exchanger.class, "exchanger", exchanger);
+ this.exchanger = exchanger;
+ }
+
+ public String getDispather() {
+ return dispather;
+ }
+
+ public void setDispather(String dispather) {
+ checkExtension(Dispather.class, "dispather", exchanger);
+ this.dispather = dispather;
+ }
+
+ public String getNetworker() {
+ return networker;
+ }
+
+ public void setNetworker(String networker) {
+ this.networker = networker;
+ }
+
+ public Integer getWait() {
+ return wait;
+ }
+
+ public void setWait(Integer wait) {
+ this.wait = wait;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java
new file mode 100644
index 0000000..f766753
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java
@@ -0,0 +1,501 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.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.config.annotation.Reference;
+import com.alibaba.dubbo.config.support.Parameter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.StaticContext;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
+import com.alibaba.dubbo.rpc.cluster.support.AvailableCluster;
+import com.alibaba.dubbo.rpc.cluster.support.ClusterUtils;
+import com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * ReferenceConfig
+ *
+ * @author william.liangf
+ */
+public class ReferenceConfig<T> extends AbstractReferenceConfig {
+
+ private static final long serialVersionUID = -5864351140409987595L;
+
+ private static final Protocol refprotocol = 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 client;
+
+ // 点对点直连服务提供地址
+ private String url;
+
+ // 方法配置
+ private List<MethodConfig> methods;
+
+ // 缺省配置
+ private ConsumerConfig consumer;
+
+ private String protocol;
+
+ // 接口代理类引用
+ 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 ReferenceConfig() {}
+
+ public ReferenceConfig(Reference reference) {
+ appendAnnotation(Reference.class, reference);
+ }
+
+ public URL toUrl() {
+ return urls == null || urls.size() == 0 ? null : urls.iterator().next();
+ }
+
+ 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();
+ appendProperties(this);
+ 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 (module == null) {
+ module = consumer.getModule();
+ }
+ if (registries == null) {
+ registries = consumer.getRegistries();
+ }
+ if (monitor == null) {
+ monitor = consumer.getMonitor();
+ }
+ }
+ if (module != null) {
+ if (registries == null) {
+ registries = module.getRegistries();
+ }
+ if (monitor == null) {
+ monitor = module.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(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
+ map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
+ map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
+ if (ConfigUtils.getPid() > 0) {
+ map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
+ }
+ if (! generic) {
+ String revision = Version.getVersion(interfaceClass, version);
+ if (revision != null && revision.length() > 0) {
+ map.put("revision", revision);
+ }
+ map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(interfaceClass).getDeclaredMethodNames())), ","));
+ }
+ map.put(Constants.INTERFACE_KEY, interfaceName);
+ appendParameters(map, application);
+ appendParameters(map, module);
+ appendParameters(map, consumer, Constants.DEFAULT_KEY);
+ appendParameters(map, this);
+ String prifix = StringUtils.getServiceKey(map);
+ if (methods != null && methods.size() > 0) {
+ for (MethodConfig method : methods) {
+ appendParameters(map, method, method.getName());
+ String retryKey = method.getName() + ".retry";
+ if (map.containsKey(retryKey)) {
+ String retryValue = map.remove(retryKey);
+ if ("false".equals(retryValue)) {
+ map.put(method.getName() + ".retries", "0");
+ }
+ }
+ appendAttributes(attributes, method, prifix + "." + method.getName());
+ checkAndConvertImplicitConfig(method, map, attributes);
+ }
+ }
+ //attributes通过系统context进行存储.
+ StaticContext.getSystemContext().putAll(attributes);
+ ref = createProxy(map);
+ }
+
+ private static void checkAndConvertImplicitConfig(MethodConfig method, Map<String,String> map, Map<Object,Object> attributes){
+ //check config conflict
+ if (Boolean.FALSE.equals(method.isReturn()) && (method.getOnreturn() != null || method.getOnthrow() != null)) {
+ throw new IllegalStateException("method config error : return attribute must be set true when onreturn or onthrow has been setted.");
+ }
+ //convert onreturn methodName to Method
+ String onReturnMethodKey = StaticContext.getKey(map,method.getName(),Constants.ON_RETURN_METHOD_KEY);
+ Object onReturnMethod = attributes.get(onReturnMethodKey);
+ if (onReturnMethod != null && onReturnMethod instanceof String){
+ attributes.put(onReturnMethodKey, getMethodByName(method.getOnreturn().getClass(), onReturnMethod.toString()));
+ }
+ //convert onthrow methodName to Method
+ String onThrowMethodKey = StaticContext.getKey(map,method.getName(),Constants.ON_THROW_METHOD_KEY);
+ Object onThrowMethod = attributes.get(onThrowMethodKey);
+ if (onThrowMethod != null && onThrowMethod instanceof String){
+ attributes.put(onThrowMethodKey, getMethodByName(method.getOnthrow().getClass(), onThrowMethod.toString()));
+ }
+ //convert oninvoke methodName to Method
+ String onInvokeMethodKey = StaticContext.getKey(map,method.getName(),Constants.ON_INVOKE_METHOD_KEY);
+ Object onInvokeMethod = attributes.get(onInvokeMethodKey);
+ if (onInvokeMethod != null && onInvokeMethod instanceof String){
+ attributes.put(onInvokeMethodKey, getMethodByName(method.getOninvoke().getClass(), onInvokeMethod.toString()));
+ }
+ }
+
+ private static Method getMethodByName(Class<?> clazz, String methodName){
+ try {
+ return ReflectUtils.findMethodByMethodName(clazz, methodName);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private T createProxy(Map<String, String> map) {
+ URL tmpUrl = new URL("temp", "localhost", 0, map);
+ final boolean isJvmRefer;
+ if (url != null && url.length() > 0) { //指定URL的情况下,不做本地引用
+ isJvmRefer = false;
+ } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
+ //默认情况下如果本地有服务暴露,则引用本地服务.
+ isJvmRefer = true;
+ } else {
+ isJvmRefer = false;
+ }
+
+ if (isJvmRefer) {
+ URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
+ invoker = refprotocol.refer(interfaceClass, url);
+ if (logger.isInfoEnabled()) {
+ logger.info("Using injvm service " + interfaceClass.getName());
+ }
+ } else {
+ if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL
+ String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
+ if (us != null && us.length > 0) {
+ for (String u : us) {
+ URL url = URL.valueOf(u);
+ if (url.getPath() == null || url.getPath().length() == 0) {
+ url = url.setPath(interfaceName);
+ }
+ if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
+ urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
+ } else {
+ urls.add(ClusterUtils.mergeUrl(url, map));
+ }
+ }
+ }
+ } else { // 通过注册中心配置拼装URL
+ List<URL> us = loadRegistries(false);
+ if (us != null && us.size() > 0) {
+ for (URL u : us) {
+ URL monitorUrl = loadMonitor(u);
+ if (monitorUrl != null) {
+ map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
+ }
+ urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
+ }
+ }
+ if (urls == null || urls.size() == 0) {
+ throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
+ }
+ }
+ if (urls.size() == 1) {
+ invoker = refprotocol.refer(interfaceClass, urls.get(0));
+ } else {
+ List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
+ URL registryURL = null;
+ for (URL url : urls) {
+ invokers.add(refprotocol.refer(interfaceClass, url));
+ if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
+ registryURL = url; // 用了最后一个registry url
+ }
+ }
+ if (registryURL != null) { // 有 注册中心协议的URL
+ // 对有注册中心的Cluster 只用 AvailableCluster
+ URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
+ invoker = cluster.join(new StaticDirectory(u, invokers));
+ } else { // 不是 注册中心的URL
+ invoker = cluster.join(new StaticDirectory(invokers));
+ }
+ }
+ }
+
+ Boolean c = check;
+ if (c == null && consumer != null) {
+ c = consumer.isCheck();
+ }
+ if (c == null) {
+ c = true; // default true
+ }
+ if (c && ! invoker.isAvailable()) {
+ throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
+ }
+ if (logger.isInfoEnabled()) {
+ logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
+ }
+ // 创建服务代理
+ return (T) proxyFactory.getProxy(invoker);
+ }
+
+ private void checkDefault() {
+ if (consumer == null) {
+ consumer = new ConsumerConfig();
+ }
+ appendProperties(consumer);
+ }
+
+ public Class<?> getInterfaceClass() {
+ if (interfaceClass != null) {
+ return interfaceClass;
+ }
+ if ((generic != null && generic.booleanValue())
+ || (consumer != null && consumer.isGeneric() != null
+ && consumer.isGeneric().booleanValue())) {
+ return GenericService.class;
+ }
+ try {
+ if (interfaceName != null && interfaceName.length() > 0) {
+ this.interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
+ .getContextClassLoader());
+ }
+ } catch (ClassNotFoundException t) {
+ throw new IllegalStateException(t.getMessage(), t);
+ }
+ return interfaceClass;
+ }
+
+ /**
+ * @deprecated
+ * @see #setInterface(Class)
+ * @param interfaceClass
+ */
+ @Deprecated
+ public void setInterfaceClass(Class<?> interfaceClass) {
+ setInterface(interfaceClass);
+ }
+
+ public String getInterface() {
+ return interfaceName;
+ }
+
+ public void setInterface(String interfaceName) {
+ this.interfaceName = interfaceName;
+ if (id == null || id.length() == 0) {
+ id = interfaceName;
+ }
+ }
+
+ public void setInterface(Class<?> interfaceClass) {
+ if (interfaceClass != null && ! interfaceClass.isInterface()) {
+ throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
+ }
+ this.interfaceClass = interfaceClass;
+ setInterface(interfaceClass == null ? (String) null : interfaceClass.getName());
+ }
+
+ public String 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;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ // just for test
+ Invoker<?> getInvoker() {
+ return invoker;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
new file mode 100644
index 0000000..225d4ea
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.config.support.Parameter;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+import com.alibaba.dubbo.remoting.Transporter;
+
+/**
+ * RegistryConfig
+ *
+ * @author william.liangf
+ */
+public class RegistryConfig extends AbstractConfig {
+
+ private static final long serialVersionUID = 5508512956753757169L;
+
+ public static final String NO_AVAILABLE = "N/A";
+
+ // 注册中心地址
+ private String address;
+
+ // 注册中心登录用户名
+ private String username;
+
+ // 注册中心登录密码
+ private String password;
+
+ // 注册中心缺省端口
+ private Integer port;
+
+ // 注册中心协议
+ private String protocol;
+
+ // 客户端实现
+ private String transporter;
+
+ private String server;
+
+ private String client;
+
+ private String group;
+
+ private String version;
+
+ // 注册中心请求超时时间(毫秒)
+ private Integer timeout;
+
+ // 注册中心会话超时时间(毫秒)
+ private Integer session;
+
+ // 动态注册中心列表存储文件
+ private String file;
+
+ // 停止时等候完成通知时间
+ private Integer wait;
+
+ // 启动时检查注册中心是否存在
+ private Boolean check;
+
+ // 在该注册中心上注册是动态的还是静态的服务
+ private Boolean dynamic;
+
+ // 在该注册中心上服务是否暴露
+ private Boolean register;
+
+ // 在该注册中心上服务是否引用
+ private Boolean subscribe;
+
+ // 自定义参数
+ private Map<String, String> parameters;
+
+ // 是否为缺省
+ private Boolean isDefault;
+
+ public RegistryConfig() {
+ }
+
+ public RegistryConfig(String address) {
+ setAddress(address);
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ checkName("protocol", protocol);
+ this.protocol = protocol;
+ }
+
+ @Parameter(excluded = true)
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ checkName("username", username);
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ checkLength("password", password);
+ this.password = password;
+ }
+
+ /**
+ * @deprecated
+ * @see com.alibaba.dubbo.config.ProviderConfig#getWait()
+ * @return wait
+ */
+ @Deprecated
+ public Integer getWait() {
+ return wait;
+ }
+
+ /**
+ * @deprecated
+ * @see com.alibaba.dubbo.config.ProviderConfig#setWait(Integer)
+ * @param wait
+ */
+ @Deprecated
+ public void setWait(Integer wait) {
+ this.wait = wait;
+ if( wait!=null && wait>0)
+ System.setProperty(Constants.SHUTDOWN_WAIT_KEY, String.valueOf(wait));
+ }
+
+ public Boolean isCheck() {
+ return check;
+ }
+
+ public void setCheck(Boolean check) {
+ this.check = check;
+ }
+
+ public String getFile() {
+ return file;
+ }
+
+ public void setFile(String file) {
+ checkPathLength("file", file);
+ this.file = file;
+ }
+
+ /**
+ * @deprecated
+ * @see #getTransporter()
+ * @return transport
+ */
+ @Deprecated
+ @Parameter(excluded = true)
+ public String getTransport() {
+ return getTransporter();
+ }
+
+ /**
+ * @deprecated
+ * @see #setTransporter(String)
+ * @param transport
+ */
+ @Deprecated
+ public void setTransport(String transport) {
+ setTransporter(transport);
+ }
+
+ public String getTransporter() {
+ return transporter;
+ }
+
+ public void setTransporter(String transporter) {
+ checkName("transporter", transporter);
+ if(transporter != null && transporter.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(transporter)){
+ throw new IllegalStateException("No such transporter type : " + transporter);
+ }
+ this.transporter = transporter;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public void setServer(String server) {
+ checkName("server", server);
+ if(server != null && server.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(server)){
+ throw new IllegalStateException("No such server type : " + server);
+ }
+ this.server = server;
+ }
+
+ public String getClient() {
+ return client;
+ }
+
+ public void setClient(String client) {
+ checkName("client", client);
+ if(client != null && client.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(client)){
+ throw new IllegalStateException("No such client type : " + client);
+ }
+ this.client = client;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public Integer getSession() {
+ return session;
+ }
+
+ public void setSession(Integer session) {
+ this.session = session;
+ }
+
+ public Boolean isDynamic() {
+ return dynamic;
+ }
+
+ public void setDynamic(Boolean dynamic) {
+ this.dynamic = dynamic;
+ }
+
+ public Boolean isRegister() {
+ return register;
+ }
+
+ public void setRegister(Boolean register) {
+ this.register = register;
+ }
+
+ public Boolean isSubscribe() {
+ return subscribe;
+ }
+
+ public void setSubscribe(Boolean subscribe) {
+ this.subscribe = subscribe;
+ }
+
+ public 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;
+ }
+
+ public Boolean isDefault() {
+ return isDefault;
+ }
+
+ public void setDefault(Boolean isDefault) {
+ this.isDefault = isDefault;
+ }
+
+ public static void destroyAll() {
+ AbstractRegistryFactory.destroyAll();
+ }
+
+ @Deprecated
+ public static void closeAll() {
+ destroyAll();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
new file mode 100644
index 0000000..40ffc41
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
@@ -0,0 +1,648 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.config.annotation.Service;
+import com.alibaba.dubbo.config.support.Parameter;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * ServiceConfig
+ *
+ * @author william.liangf
+ */
+public class ServiceConfig<T> extends AbstractServiceConfig {
+
+ private static final long serialVersionUID = 3033787999037024738L;
+
+ private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ // 接口类型
+ private String interfaceName;
+
+ private Class<?> interfaceClass;
+
+ // 接口实现类引用
+ private T ref;
+
+ // 服务名称
+ private String path;
+
+ // 方法配置
+ private 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 ServiceConfig() {
+ }
+
+ public ServiceConfig(Service service) {
+ appendAnnotation(Service.class, service);
+ }
+
+ public URL toUrl() {
+ return urls == null || urls.size() == 0 ? null : urls.iterator().next();
+ }
+
+ public List<URL> toUrls() {
+ return urls;
+ }
+
+ public synchronized void export() {
+ if (provider != null) {
+ if (export == null) {
+ export = provider.getExport();
+ }
+ if (delay == null) {
+ delay = provider.getDelay();
+ }
+ }
+ if (export != null && ! export.booleanValue()) {
+ return;
+ }
+ 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 (module == null) {
+ module = provider.getModule();
+ }
+ if (registries == null) {
+ registries = provider.getRegistries();
+ }
+ if (monitor == null) {
+ monitor = provider.getMonitor();
+ }
+ if (protocols == null) {
+ protocols = provider.getProtocols();
+ }
+ }
+ if (module != null) {
+ if (registries == null) {
+ registries = module.getRegistries();
+ }
+ if (monitor == null) {
+ monitor = module.getMonitor();
+ }
+ }
+ 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();
+ appendProperties(this);
+ 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(true);
+ for (ProtocolConfig protocolConfig : protocols) {
+ String name = protocolConfig.getName();
+ if (name == null || name.length() == 0) {
+ name = "dubbo";
+ }
+ String host = protocolConfig.getHost();
+ if (provider != null && (host == null || host.length() == 0)) {
+ host = provider.getHost();
+ }
+ boolean anyhost = false;
+ if (NetUtils.isInvalidLocalHost(host)) {
+ anyhost = true;
+ try {
+ host = InetAddress.getLocalHost().getHostAddress();
+ } catch (UnknownHostException e) {
+ logger.warn(e.getMessage(), e);
+ }
+ if (NetUtils.isInvalidLocalHost(host)) {
+ if (registryURLs != null && registryURLs.size() > 0) {
+ for (URL registryURL : registryURLs) {
+ try {
+ Socket socket = new Socket();
+ try {
+ SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
+ socket.connect(addr, 1000);
+ host = socket.getLocalAddress().getHostAddress();
+ break;
+ } finally {
+ try {
+ socket.close();
+ } catch (Throwable e) {}
+ }
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ if (NetUtils.isInvalidLocalHost(host)) {
+ host = NetUtils.getLocalHost();
+ }
+ }
+ }
+ Integer port = protocolConfig.getPort();
+ if (provider != null && (port == null || port == 0)) {
+ port = provider.getPort();
+ }
+ if (port == null || port == 0) {
+ port = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();
+ }
+ if (port == null || port <= 0) {
+ port = NetUtils.getAvailablePort();
+ logger.warn("Use random available port(" + port + ") for protocol " + name);
+ }
+ Map<String, String> map = new HashMap<String, String>();
+ if (anyhost) {
+ map.put(Constants.ANYHOST_KEY, "true");
+ }
+ map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
+ map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
+ map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
+ if (ConfigUtils.getPid() > 0) {
+ map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
+ }
+ appendParameters(map, application);
+ appendParameters(map, module);
+ appendParameters(map, provider, Constants.DEFAULT_KEY);
+ appendParameters(map, protocolConfig);
+ appendParameters(map, this);
+ if (methods != null && methods.size() > 0) {
+ for (MethodConfig method : methods) {
+ appendParameters(map, method, method.getName());
+ String retryKey = method.getName() + ".retry";
+ if (map.containsKey(retryKey)) {
+ String retryValue = map.remove(retryKey);
+ if ("false".equals(retryValue)) {
+ map.put(method.getName() + ".retries", "0");
+ }
+ }
+ List<ArgumentConfig> arguments = method.getArguments();
+ if (arguments != null && arguments.size() > 0) {
+ for (ArgumentConfig argument : arguments) {
+ //类型自动转换.
+ if(argument.getType() != null && argument.getType().length() >0){
+ Method[] methods = interfaceClass.getMethods();
+ //遍历所有方法
+ if(methods != null && methods.length > 0){
+ for (int i = 0; i < methods.length; i++) {
+ String methodName = methods[i].getName();
+ //匹配方法名称,获取方法签名.
+ if(methodName.equals(method.getName())){
+ Class<?>[] argtypes = methods[i].getParameterTypes();
+ //一个方法中单个callback
+ if (argument.getIndex() != -1 ){
+ if (argtypes[argument.getIndex()].getName().equals(argument.getType())){
+ appendParameters(map, argument, method.getName() + "." + argument.getIndex());
+ }else {
+ throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
+ }
+ } else {
+ //一个方法中多个callback
+ for (int j = 0 ;j<argtypes.length ;j++) {
+ Class<?> argclazz = argtypes[j];
+ if (argclazz.getName().equals(argument.getType())){
+ appendParameters(map, argument, method.getName() + "." + j);
+ if (argument.getIndex() != -1 && argument.getIndex() != j){
+ throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }else if(argument.getIndex() != -1){
+ appendParameters(map, argument, method.getName() + "." + argument.getIndex());
+ }else {
+ throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
+ }
+
+ }
+ }
+ }
+ }
+ if (generic) {
+ map.put("generic", String.valueOf(true));
+ map.put("methods", Constants.ANY_VALUE);
+ } else {
+ String revision = Version.getVersion(interfaceClass, version);
+ if (revision != null && revision.length() > 0) {
+ map.put("revision", revision);
+ }
+ map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(interfaceClass).getDeclaredMethodNames())), ","));
+ }
+ if (! ConfigUtils.isEmpty(token)) {
+ if (ConfigUtils.isDefault(token)) {
+ map.put("token", UUID.randomUUID().toString());
+ } else {
+ map.put("token", token);
+ }
+ }
+ if ("injvm".equals(protocolConfig.getName())) {
+ protocolConfig.setRegister(false);
+ map.put("notify", "false");
+ }
+ // 导出服务
+ String contextPath = protocolConfig.getContextpath();
+ if ((contextPath == null || contextPath.length() == 0) && provider != null) {
+ contextPath = provider.getContextpath();
+ }
+ URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
+
+ String scope = url.getParameter(Constants.SCOPE_KEY);
+ //配置为none不暴露
+ if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
+ //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
+ if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
+ exportLocal(url);
+ }
+ //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
+ if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
+ 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 = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
+ URL monitorUrl = loadMonitor(registryURL);
+ if (monitorUrl != null) {
+ url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
+ }
+ if (logger.isInfoEnabled()) {
+ logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
+ }
+ Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
+ 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);
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private void exportLocal(URL url) {
+ if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
+ URL local = URL.valueOf(url.toFullString())
+ .setProtocol(Constants.LOCAL_PROTOCOL)
+ .setHost(NetUtils.LOCALHOST)
+ .setPort(0);
+ Exporter<?> exporter = protocol.export(
+ proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
+ exporters.add(exporter);
+ logger.info("Export dubbo service " + interfaceClass.getName() +" to local registry");
+ }
+ }
+
+ private void checkDefault() {
+ if (provider == null) {
+ provider = new ProviderConfig();
+ }
+ appendProperties(provider);
+ }
+
+ private void checkProtocol() {
+ if ((protocols == null || protocols.size() == 0)
+ && provider != null) {
+ setProtocols(provider.getProtocols());
+ }
+ // 兼容旧版本
+ if (protocols == null || protocols.size() == 0) {
+ setProtocol(new ProtocolConfig());
+ }
+ for (ProtocolConfig protocolConfig : protocols) {
+ appendProperties(protocolConfig);
+ }
+ }
+
+ public Class<?> getInterfaceClass() {
+ if (interfaceClass != null) {
+ return interfaceClass;
+ }
+ if (ref instanceof GenericService) {
+ return GenericService.class;
+ }
+ try {
+ if (interfaceName != null && interfaceName.length() > 0) {
+ this.interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
+ .getContextClassLoader());
+ }
+ } catch (ClassNotFoundException t) {
+ throw new IllegalStateException(t.getMessage(), t);
+ }
+ return interfaceClass;
+ }
+
+ /**
+ * @deprecated
+ * @see #setInterface(Class)
+ * @param interfaceClass
+ */
+ public void setInterfaceClass(Class<?> interfaceClass) {
+ setInterface(interfaceClass);
+ }
+
+ public String getInterface() {
+ return interfaceName;
+ }
+
+ public void setInterface(String interfaceName) {
+ this.interfaceName = interfaceName;
+ if (id == null || id.length() == 0) {
+ id = interfaceName;
+ }
+ }
+
+ public void setInterface(Class<?> interfaceClass) {
+ if (interfaceClass != null && ! interfaceClass.isInterface()) {
+ throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
+ }
+ this.interfaceClass = interfaceClass;
+ setInterface(interfaceClass == null ? (String) null : interfaceClass.getName());
+ }
+
+ public T getRef() {
+ return ref;
+ }
+
+ public void setRef(T ref) {
+ this.ref = ref;
+ }
+
+ @Parameter(excluded = true)
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ checkPathName("path", path);
+ this.path = path;
+ }
+
+ public 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/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Reference.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Reference.java
new file mode 100644
index 0000000..ec7ee9a
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Reference.java
@@ -0,0 +1,114 @@
+/*
+ * 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.annotation;
+
+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;
+
+/**
+ * Reference
+ *
+ * @author william.liangf
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface Reference {
+
+ Class<?> interfaceClass() default void.class;
+
+ String interfaceName() default "";
+
+ String version() default "";
+
+ String group() default "";
+
+ String url() default "";
+
+ String client() default "";
+
+ boolean generic() default false;
+
+ boolean injvm() default false;
+
+ boolean check() default false;
+
+ boolean init() default false;
+
+ boolean lazy() default false;
+
+ boolean stubevent() default false;
+
+ String reconnect() default "";
+
+ boolean sticky() default false;
+
+ String proxy() default "";
+
+ String stub() default "";
+
+ String cluster() default "";
+
+ int connections() default 0;
+
+ int callbacks() default 0;
+
+ String onconnect() default "";
+
+ String ondisconnect() default "";
+
+ String owner() default "";
+
+ String layer() default "";
+
+ int retries() default 0;
+
+ String loadbalance() default "";
+
+ boolean async() default false;
+
+ int actives() default 0;
+
+ boolean sent() default false;
+
+ String mock() default "";
+
+ String validation() default "";
+
+ int timeout() default 0;
+
+ String cache() default "";
+
+ String[] filter() default {};
+
+ String[] listener() default {};
+
+ String[] parameters() default {};
+
+ String application() default "";
+
+ String module() default "";
+
+ String consumer() default "";
+
+ String monitor() default "";
+
+ String[] registry() default {};
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Service.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Service.java
new file mode 100644
index 0000000..789ad37
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Service.java
@@ -0,0 +1,120 @@
+/*
+ * 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.annotation;
+
+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;
+
+/**
+ * Service
+ *
+ * @author william.liangf
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Service {
+
+ Class<?> interfaceClass() default void.class;
+
+ String interfaceName() default "";
+
+ String version() default "";
+
+ String group() default "";
+
+ String path() default "";
+
+ boolean export() default false;
+
+ String token() default "";
+
+ boolean deprecated() default false;
+
+ boolean dynamic() default false;
+
+ String accesslog() default "";
+
+ int executes() default 0;
+
+ boolean register() default false;
+
+ int weight() default 0;
+
+ String document() default "";
+
+ int delay() default 0;
+
+ String local() default "";
+
+ String stub() default "";
+
+ String cluster() default "";
+
+ String proxy() default "";
+
+ int connections() default 0;
+
+ int callbacks() default 0;
+
+ String onconnect() default "";
+
+ String ondisconnect() default "";
+
+ String owner() default "";
+
+ String layer() default "";
+
+ int retries() default 0;
+
+ String loadbalance() default "";
+
+ boolean async() default false;
+
+ int actives() default 0;
+
+ boolean sent() default false;
+
+ String mock() default "";
+
+ String validation() default "";
+
+ int timeout() default 0;
+
+ String cache() default "";
+
+ String[] filter() default {};
+
+ String[] listener() default {};
+
+ String[] parameters() default {};
+
+ String application() default "";
+
+ String module() default "";
+
+ String provider() default "";
+
+ String[] protocol() default {};
+
+ String monitor() default "";
+
+ String[] registry() default {};
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/support/Parameter.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/support/Parameter.java
new file mode 100644
index 0000000..5279261
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/support/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.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;
+
+/**
+ * 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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/GenericServiceTest.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/GenericServiceTest.java
new file mode 100644
index 0000000..5d39423
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/GenericServiceTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.config.api.DemoException;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.api.User;
+import com.alibaba.dubbo.config.provider.impl.DemoServiceImpl;
+import com.alibaba.dubbo.rpc.service.GenericException;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * GenericServiceTest
+ *
+ * @author william.liangf
+ */
+public class GenericServiceTest {
+
+ @Test
+ public void testGenericServiceException() {
+ ServiceConfig<GenericService> service = new ServiceConfig<GenericService>();
+ service.setApplication(new ApplicationConfig("generic-provider"));
+ service.setRegistry(new RegistryConfig("N/A"));
+ service.setProtocol(new ProtocolConfig("dubbo", 29581));
+ service.setInterface(DemoService.class.getName());
+ service.setRef(new GenericService() {
+ public Object $invoke(String method, String[] parameterTypes, Object[] args)
+ throws GenericException {
+ if ("sayName".equals(method)) {
+ return "Generic " + args[0];
+ }
+ if ("throwDemoException".equals(method)) {
+ throw new GenericException(DemoException.class.getName(), "Generic");
+ }
+ if ("getUsers".equals(method)) {
+ return args[0];
+ }
+ return null;
+ }
+ });
+ service.export();
+ try {
+ ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
+ reference.setApplication(new ApplicationConfig("generic-consumer"));
+ reference.setInterface(DemoService.class);
+ reference.setUrl("dubbo://127.0.0.1:29581?generic=true");
+ DemoService demoService = reference.get();
+ try {
+ // say name
+ Assert.assertEquals("Generic Haha", demoService.sayName("Haha"));
+ // get users
+ List<User> users = new ArrayList<User>();
+ users.add(new User("Aaa"));
+ users = demoService.getUsers(users);
+ Assert.assertEquals("Aaa", users.get(0).getName());
+ // throw demo exception
+ try {
+ demoService.throwDemoException();
+ Assert.fail();
+ } catch (DemoException e) {
+ Assert.assertEquals("Generic", e.getMessage());
+ }
+ } finally {
+ reference.destroy();
+ }
+ } finally {
+ service.unexport();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testGenericReferenceException() {
+ ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
+ service.setApplication(new ApplicationConfig("generic-provider"));
+ service.setRegistry(new RegistryConfig("N/A"));
+ service.setProtocol(new ProtocolConfig("dubbo", 29581));
+ service.setInterface(DemoService.class.getName());
+ service.setRef(new DemoServiceImpl());
+ service.export();
+ try {
+ ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
+ reference.setApplication(new ApplicationConfig("generic-consumer"));
+ reference.setInterface(DemoService.class);
+ reference.setUrl("dubbo://127.0.0.1:29581");
+ reference.setGeneric(true);
+ GenericService genericService = reference.get();
+ try {
+ List<Map<String, Object>> users = new ArrayList<Map<String, Object>>();
+ Map<String, Object> user = new HashMap<String, Object>();
+ user.put("class", "com.alibaba.dubbo.config.api.User");
+ user.put("name", "actual.provider");
+ users.add(user);
+ users = (List<Map<String, Object>>) genericService.$invoke("getUsers", new String[] {List.class.getName()}, new Object[] {users});
+ Assert.assertEquals(1, users.size());
+ Assert.assertEquals("actual.provider", users.get(0).get("name"));
+ } finally {
+ reference.destroy();
+ }
+ } finally {
+ service.unexport();
+ }
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/RpcConfigGetSetProxy.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/RpcConfigGetSetProxy.java
new file mode 100644
index 0000000..7108ae0
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/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;
+
+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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/Box.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/Box.java
new file mode 100644
index 0000000..d1f381d
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/Box.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.api;
+
+/**
+ * @author ding.lid
+ */
+public interface Box {
+
+ String getName();
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/DemoException.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/DemoException.java
new file mode 100644
index 0000000..d9ce796
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/DemoService.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/DemoService.java
new file mode 100644
index 0000000..8615a26
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/DemoService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.api;
+
+import java.util.List;
+
+
+/**
+ * DemoService
+ *
+ * @author william.liangf
+ */
+public interface DemoService {
+
+ String sayName(String name);
+
+ Box getBox();
+
+ void throwDemoException() throws DemoException;
+
+ List<User> getUsers(List<User> users);
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/User.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/User.java
new file mode 100644
index 0000000..249a0ee
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/api/User.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.api;
+
+import java.io.Serializable;
+
+/**
+ * User
+ *
+ * @author william.liangf
+ */
+public class User implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+
+ public User() {
+ }
+
+ public User(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/cache/CacheService.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/cache/CacheService.java
new file mode 100644
index 0000000..5e5fe52
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/cache/CacheService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.cache;
+
+/**
+ * ValidationService
+ *
+ * @author william.liangf
+ */
+public interface CacheService {
+
+ String findCache(String id);
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/cache/CacheServiceImpl.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/cache/CacheServiceImpl.java
new file mode 100644
index 0000000..91d4a40
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/cache/CacheServiceImpl.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.cache;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * ValidationServiceImpl
+ *
+ * @author william.liangf
+ */
+public class CacheServiceImpl implements CacheService {
+
+ private final AtomicInteger i = new AtomicInteger();
+
+ public String findCache(String id) {
+ return "request: " + id + ", response: " + i.getAndIncrement();
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/cache/CacheTest.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/cache/CacheTest.java
new file mode 100644
index 0000000..eab1722
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/cache/CacheTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.cache;
+
+import junit.framework.TestCase;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.ServiceConfig;
+
+/**
+ * CacheTest
+ *
+ * @author william.liangf
+ */
+public class CacheTest extends TestCase {
+
+ @Test
+ public void testCache() throws Exception {
+ ServiceConfig<CacheService> service = new ServiceConfig<CacheService>();
+ service.setApplication(new ApplicationConfig("cache-provider"));
+ service.setRegistry(new RegistryConfig("N/A"));
+ service.setProtocol(new ProtocolConfig("dubbo", 29582));
+ service.setInterface(CacheService.class.getName());
+ service.setRef(new CacheServiceImpl());
+ service.export();
+ try {
+ ReferenceConfig<CacheService> reference = new ReferenceConfig<CacheService>();
+ reference.setApplication(new ApplicationConfig("cache-consumer"));
+ reference.setInterface(CacheService.class);
+ reference.setUrl("dubbo://127.0.0.1:29582?scope=remote&cache=true");
+ CacheService cacheService = reference.get();
+ try {
+ // 测试缓存生效,多次调用返回同样的结果。(服务器端自增长返回值)
+ String fix = null;
+ for (int i = 0; i < 3; i ++) {
+ String result = cacheService.findCache("0");
+ Assert.assertTrue(fix == null || fix.equals(result));
+ fix = result;
+ Thread.sleep(100);
+ }
+
+ // LRU的缺省cache.size为1000,执行1001次,应有溢出
+ for (int n = 0; n < 1001; n ++) {
+ String pre = null;
+ for (int i = 0; i < 10; i ++) {
+ String result = cacheService.findCache(String.valueOf(n));
+ Assert.assertTrue(pre == null || pre.equals(result));
+ pre = result;
+ }
+ }
+
+ // 测试LRU有移除最开始的一个缓存项
+ String result = cacheService.findCache("0");
+ Assert.assertFalse(fix == null || fix.equals(result));
+ } finally {
+ reference.destroy();
+ }
+ } finally {
+ service.unexport();
+ }
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionByAnnotation.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionByAnnotation.java
new file mode 100644
index 0000000..5fad4af
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionBySetter.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/consumer/DemoActionBySetter.java
new file mode 100644
index 0000000..a345bde
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/consumer/DemoInterceptor.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/consumer/DemoInterceptor.java
new file mode 100644
index 0000000..e15a76e
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl.java
new file mode 100644
index 0000000..2d1dd9a
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.provider.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.config.api.Box;
+import com.alibaba.dubbo.config.api.DemoException;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.api.User;
+
+/**
+ * DemoServiceImpl
+ *
+ * @author william.liangf
+ */
+public class DemoServiceImpl implements DemoService {
+
+ public String sayName(String name) {
+ return "say:" + name;
+ }
+
+ public Box getBox() {
+ return null;
+ }
+
+ public void throwDemoException() throws DemoException {
+ throw new DemoException("DemoServiceImpl");
+ }
+
+ public List<User> getUsers(List<User> users) {
+ return users;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl_LongWaiting.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl_LongWaiting.java
new file mode 100644
index 0000000..cb4a52b
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl_LongWaiting.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.provider.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.config.api.Box;
+import com.alibaba.dubbo.config.api.DemoException;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.api.User;
+
+/**
+ * DemoServiceImpl
+ *
+ * @author william.liangf
+ */
+public class DemoServiceImpl_LongWaiting implements DemoService {
+
+ public String sayName(String name) {
+ try {
+ Thread.sleep(100 * 1000);
+ } catch (InterruptedException e) {}
+
+ return "say:" + name;
+ }
+
+ public Box getBox() {
+ return null;
+ }
+
+ public void throwDemoException() throws DemoException {
+ throw new DemoException("LongWaiting");
+ }
+
+ public List<User> getUsers(List<User> users) {
+ return users;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBox.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBox.java
new file mode 100644
index 0000000..ca45a69
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBoxDemoServiceImpl.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBoxDemoServiceImpl.java
new file mode 100644
index 0000000..118d934
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/provider/impl/UnserializableBoxDemoServiceImpl.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.provider.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.config.api.Box;
+import com.alibaba.dubbo.config.api.DemoException;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.api.User;
+
+/**
+ * DemoServiceImpl
+ *
+ * @author william.liangf
+ */
+public class UnserializableBoxDemoServiceImpl implements DemoService {
+
+ public String sayName(String name) {
+ return "say:" + name;
+ }
+
+ public Box getBox() {
+ return new UnserializableBox();
+ }
+
+ public void throwDemoException() throws DemoException {
+ throw new DemoException("Unserializable");
+ }
+
+ public List<User> getUsers(List<User> users) {
+ return users;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.java
new file mode 100644
index 0000000..332556b
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ *
+ * @author haomin.liuhm
+ *
+ */
+public class MockProtocol implements Protocol {
+
+ /* (non-Javadoc)
+ * @see com.alibaba.dubbo.rpc.Protocol#getDefaultPort()
+ */
+ public int getDefaultPort() {
+
+ return 0;
+ }
+
+ /* (non-Javadoc)
+ * @see com.alibaba.dubbo.rpc.Protocol#export(com.alibaba.dubbo.rpc.Invoker)
+ */
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see com.alibaba.dubbo.rpc.Protocol#refer(java.lang.Class, com.alibaba.dubbo.common.URL)
+ */
+ public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+
+ final URL u = url;
+
+ return new Invoker<T>(){
+ public Class<T> getInterface(){
+ return null;
+ }
+ public URL getUrl(){
+ return u;
+ }
+ public boolean isAvailable(){
+ return true;
+ }
+ public Result invoke(Invocation invocation) throws RpcException{
+ return null;
+ }
+
+ public void destroy(){
+
+ }
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see com.alibaba.dubbo.rpc.Protocol#destroy()
+ */
+ public void destroy() {
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/support/MockRegistry.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/support/MockRegistry.java
new file mode 100644
index 0000000..81623c5
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/support/MockRegistry.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.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")
+ .removeParameter(Constants.CATEGORY_KEY)
+ .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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.java
new file mode 100644
index 0000000..f3dedbf
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * TODO Comment of MockRegistryFactory
+ * @author haomin.liuhm
+ *
+ */
+public class MockRegistryFactory implements RegistryFactory {
+
+ /*
+ * @see com.alibaba.dubbo.registry.RegistryFactory#getRegistry(com.alibaba.dubbo.common.URL)
+ */
+ public Registry getRegistry(URL url) {
+
+ return new MockRegistry();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/url/test/ExporterSideConfigUrlTest.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/url/test/ExporterSideConfigUrlTest.java
new file mode 100644
index 0000000..5df4864
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/url/test/InvokerSideConfigUrlTest.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/url/test/InvokerSideConfigUrlTest.java
new file mode 100644
index 0000000..30fdcc8
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/url/test/InvokerSideConfigUrlTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ConsumerConfig;
+import com.alibaba.dubbo.config.MethodConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.support.MockRegistry;
+
+
+/**
+ * @author haomin.liuhm
+ *
+ */
+public class InvokerSideConfigUrlTest extends UrlTestBase {
+ private static final Logger log = LoggerFactory.getLogger(InvokerSideConfigUrlTest.class);
+
+ // ======================================================
+ // invoker related data preparing
+ // ======================================================
+ private ApplicationConfig appConfForConsumer;
+ private ApplicationConfig appConfForReference;
+ private RegistryConfig regConfForConsumer;
+ private RegistryConfig regConfForReference;
+ private MethodConfig methodConfForReference;
+ private ConsumerConfig consumerConf;
+ private ReferenceConfig<DemoService> refConf;
+
+ private Object appConfForConsumerTable[][] = {
+ {"", "", "", "", "", "", "", "", "", ""},
+ };
+
+ private Object appConfForReferenceTable[][] = {
+ {"", "", "", "", "", "", "", "", "", ""},
+ };
+
+ private Object regConfForConsumerTable[][] = {
+// {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""},
+// {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""},
+// {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""},
+// {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""},
+ {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""},
+ {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+ };
+
+ private Object regConfForReferenceTable[][] = {
+ {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""},
+ {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""},
+ {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""},
+ {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""},
+ {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""},
+ {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+ };
+
+ private Object methodConfForReferenceTable[][] = {
+ {"actives", "eatTiger.actives", "int", 0, 90, "", "", "", "", ""},
+ {"executes", "eatTiger.executes", "int", 0, 90, "", "", "", "", ""},
+ {"deprecated", "eatTiger.deprecated", "boolean", false, true, "", "", "", "", ""},
+ {"async", "eatTiger.async", "boolean", false, true, "", "", "", "", ""},
+ {"timeout", "eatTiger.timeout", "int", 0, 90, "", "", "", "", ""},
+ };
+
+ private Object refConfTable[][] = {
+// {"version", "version", "string", "0.0.0", "1.2.3", "", "", "", "", ""},
+// {"group", "group", "string", "", "HaominTest", "", "", "", "", ""},
+
+// {"delay", "delay", "int", 0, 5, "", "", "", "", ""}, // not boolean
+ {"timeout", "timeout", "int", 5000, 3000, "", "", "", "", ""},
+ {"retries", "retries", "int", 2, 5, "", "", "", "", ""},
+ {"connections", "connections", "boolean", 100, 20, "", "", "", "", ""},
+ {"loadbalance", "loadbalance", "string", "random", "roundrobin", "leastactive", "", "", ""},
+ {"async", "async", "boolean", false, true, "", "", "", "", ""},
+ //excluded = true
+// {"generic", "generic", "boolean", false, true, "", "", "", "", ""},
+ {"check", "check", "boolean", false, true, "", "", "", "", ""},
+ //{"local", "local", "string", "false", "HelloServiceLocal", "true", "", "", "", ""},
+ //{"local", "local", "string", "false", "true", "", "", "", "", ""},
+ //{"mock", "mock", "string", "false", "dubbo.test.HelloServiceMock", "true", "", "", "", ""},
+ {"mock", "mock", "string", "false", "false", "", "", "", "", ""},
+ {"proxy", "proxy", "boolean", "javassist", "jdk", "", "", "", "", ""},
+ {"client", "client", "string", "netty", "mina", "", "", "", "", ""},
+ {"client", "client", "string", "netty", "mina", "", "", "", "", ""},
+ {"owner", "owner", "string", "", "haomin/ludvik", "", "", "", "", ""},
+ {"actives", "actives", "int", 0, 30, "", "", "", "", ""},
+ {"cluster", "cluster", "string", "failover", "failfast", "failsafe", "failback", "forking", "", ""},
+ //excluded = true
+// {"filter", "service.filter", "string", "default", "-generic", "", "", "", "", ""},
+ //excluded = true
+// {"listener", "exporter.listener", "string", "default", "-deprecated", "", "", "", "", ""},
+ //{"", "", "", "", "", "", "", "", "", ""},
+ };
+
+ private Object consumerConfTable[][] = {
+ {"timeout", "default.timeout", "int", 5000, 8000, "", "", "", "", ""},
+ {"retries", "default.retries", "int", 2, 5, "", "", "", "", ""},
+ {"loadbalance", "default.loadbalance", "string", "random", "leastactive", "", "", "", "", ""},
+ {"async", "default.async", "boolean", false, true, "", "", "", "", ""},
+ {"connections", "default.connections", "int", 100, 5, "", "", "", "", ""},
+// {"generic", "generic", "boolean", false, false, "", "", "", "", ""},
+ {"check", "check", "boolean", true, false, "", "", "", "", ""},
+ {"proxy", "proxy", "string", "javassist", "jdk", "javassist", "", "", "", ""},
+ {"owner", "owner", "string", "", "haomin", "", "", "", "", ""},
+ {"actives", "default.actives", "int", 0, 5, "", "", "", "", ""},
+ {"cluster", "default.cluster", "string", "failover", "forking", "", "", "", "", ""},
+ {"filter", "", "string", "", "", "", "", "", "", ""},
+ {"listener", "", "string", "", "", "", "", "", "", ""},
+// {"", "", "", "", "", "", "", "", "", ""},
+ };
+
+ // ======================================================
+ // test Start
+ // ======================================================
+
+ @BeforeClass
+ public static void start(){
+
+ //RegistryController.startRegistryIfAbsence(1);
+ }
+
+
+ @Before
+ public void setUp(){
+
+ initServConf();
+ initRefConf();
+
+ return;
+ }
+
+ @After()
+ public void teardown() {
+
+ //RegistryServer.reloadCache();
+ }
+
+
+ @Test
+ public void consumerConfUrlTest(){
+ verifyInvokerUrlGeneration(consumerConf, consumerConfTable);
+ }
+
+ @Test
+ public void refConfUrlTest(){
+ verifyInvokerUrlGeneration(refConf, refConfTable);
+ }
+
+ @Ignore
+ @Test //注册中心的参数不会在与consumer的query merge
+ public void regConfForConsumerUrlTest(){
+
+ verifyInvokerUrlGeneration(regConfForConsumer, regConfForConsumerTable);
+
+ }
+
+ // ======================================================
+ // private helper
+ // ======================================================
+ private void initRefConf(){
+
+ appConfForConsumer = new ApplicationConfig();
+ appConfForReference = new ApplicationConfig();
+ regConfForConsumer = new RegistryConfig();
+ regConfForReference = new RegistryConfig();
+ methodConfForReference = new MethodConfig();
+
+ refConf = new ReferenceConfig<DemoService>();
+ consumerConf = new ConsumerConfig();
+
+ methodConfForReference.setName("sayName");
+ regConfForReference.setAddress("127.0.0.1:9090");
+ regConfForReference.setProtocol("mockregistry");
+ appConfForReference.setName("ConfigTests");
+ refConf.setInterface("com.alibaba.dubbo.config.api.DemoService");
+
+ refConf.setApplication(appConfForReference);
+ consumerConf.setApplication(appConfForConsumer);
+
+ refConf.setRegistry(regConfForReference);
+ consumerConf.setRegistry(regConfForConsumer);
+
+ refConf.setConsumer(consumerConf);
+
+ refConf.setMethods(Arrays.asList(new MethodConfig[]{methodConfForReference}));
+
+ refConf.setScope(Constants.SCOPE_REMOTE);
+ }
+
+ 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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/url/test/UrlTestBase.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/url/test/UrlTestBase.java
new file mode 100644
index 0000000..cbffa3d
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/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.RpcConfigGetSetProxy;
+import com.alibaba.dubbo.config.ServiceConfig;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.provider.impl.DemoServiceImpl;
+
+/**
+ * @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/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationParameter.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationParameter.java
new file mode 100644
index 0000000..9b75005
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationParameter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.validation;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.validation.constraints.Future;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Past;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+/**
+ * ValidationParameter
+ *
+ * @author william.liangf
+ */
+public class ValidationParameter implements Serializable {
+
+ private static final long serialVersionUID = 7158911668568000392L;
+
+ @NotNull // 不允许为空
+ @Size(min = 2, max = 20) // 长度或大小范围
+ private String name;
+
+ @NotNull(groups = ValidationService.Save.class) // 保存时不允许为空,更新时允许为空 ,表示不更新该字段
+ @Pattern(regexp = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$")
+ private String email;
+
+ @Min(18) // 最小值
+ @Max(100) // 最大值
+ private int age;
+
+ @Past // 必须为一个过去的时间
+ private Date loginDate;
+
+ @Future // 必须为一个未来的时间
+ private Date expiryDate;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public Date getLoginDate() {
+ return loginDate;
+ }
+
+ public void setLoginDate(Date loginDate) {
+ this.loginDate = loginDate;
+ }
+
+ public Date getExpiryDate() {
+ return expiryDate;
+ }
+
+ public void setExpiryDate(Date expiryDate) {
+ this.expiryDate = expiryDate;
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationService.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationService.java
new file mode 100644
index 0000000..3120be0
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationService.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.validation;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+
+/**
+ * ValidationService
+ *
+ * @author william.liangf
+ */
+public interface ValidationService { // 缺省可按服务接口区分验证场景,如:@NotNull(groups = ValidationService.class)
+
+ @interface Save{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Save.class),可选
+ void save(ValidationParameter parameter);
+
+ @interface Update{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Update.class),可选
+ void update(ValidationParameter parameter);
+
+ void delete(@Min(1) long id, @NotNull @Size(min = 2, max = 16) @Pattern(regexp = "^[a-zA-Z]+$") String operator);
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationServiceImpl.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationServiceImpl.java
new file mode 100644
index 0000000..6a47f2d
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationServiceImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.validation;
+
+/**
+ * ValidationServiceImpl
+ *
+ * @author william.liangf
+ */
+public class ValidationServiceImpl implements ValidationService {
+
+ public void save(ValidationParameter parameter) {
+ }
+
+ public void update(ValidationParameter parameter) {
+ }
+
+ public void delete(long id, String operator) {
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationTest.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationTest.java
new file mode 100644
index 0000000..2b4e74b
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/validation/ValidationTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.validation;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.ServiceConfig;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * GenericServiceTest
+ *
+ * @author william.liangf
+ */
+public class ValidationTest {
+
+ @Test
+ public void testValidation() {
+ ServiceConfig<ValidationService> service = new ServiceConfig<ValidationService>();
+ service.setApplication(new ApplicationConfig("validation-provider"));
+ service.setRegistry(new RegistryConfig("N/A"));
+ service.setProtocol(new ProtocolConfig("dubbo", 29582));
+ service.setInterface(ValidationService.class.getName());
+ service.setRef(new ValidationServiceImpl());
+ service.setValidation(String.valueOf(true));
+ service.export();
+ try {
+ ReferenceConfig<ValidationService> reference = new ReferenceConfig<ValidationService>();
+ reference.setApplication(new ApplicationConfig("validation-consumer"));
+ reference.setInterface(ValidationService.class);
+ reference.setUrl("dubbo://127.0.0.1:29582?scope=remote&validation=true");
+ ValidationService validationService = reference.get();
+ try {
+ // Save OK
+ ValidationParameter parameter = new ValidationParameter();
+ parameter.setName("liangfei");
+ parameter.setEmail("liangfei@liang.fei");
+ parameter.setAge(50);
+ parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));
+ parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));
+ validationService.save(parameter);
+
+ try {
+ parameter = new ValidationParameter();
+ parameter.setName("l");
+ parameter.setEmail("liangfei@liang.fei");
+ parameter.setAge(50);
+ parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));
+ parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));
+ validationService.save(parameter);
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ }
+
+ // Save Error
+ try {
+ parameter = new ValidationParameter();
+ validationService.save(parameter);
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ }
+
+ // Delete OK
+ validationService.delete(2, "abc");
+
+ // Delete Error
+ try {
+ validationService.delete(2, "a");
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ Assert.assertEquals(1, violations.size());
+ }
+
+ // Delete Error
+ try {
+ validationService.delete(0, "abc");
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ Assert.assertEquals(1, violations.size());
+ }
+ try {
+ validationService.delete(2, null);
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ Assert.assertEquals(1, violations.size());
+ }
+ try {
+ validationService.delete(0, null);
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ Assert.assertEquals(2, violations.size());
+ }
+ } finally {
+ reference.destroy();
+ }
+ } finally {
+ service.unexport();
+ }
+ }
+
+ @Test
+ public void testProviderValidation() {
+ ServiceConfig<ValidationService> service = new ServiceConfig<ValidationService>();
+ service.setApplication(new ApplicationConfig("validation-provider"));
+ service.setRegistry(new RegistryConfig("N/A"));
+ service.setProtocol(new ProtocolConfig("dubbo", 29582));
+ service.setInterface(ValidationService.class.getName());
+ service.setRef(new ValidationServiceImpl());
+ service.setValidation(String.valueOf(true));
+ service.export();
+ try {
+ ReferenceConfig<ValidationService> reference = new ReferenceConfig<ValidationService>();
+ reference.setApplication(new ApplicationConfig("validation-consumer"));
+ reference.setInterface(ValidationService.class);
+ reference.setUrl("dubbo://127.0.0.1:29582?scope=remote");
+ ValidationService validationService = reference.get();
+ try {
+ // Save OK
+ ValidationParameter parameter = new ValidationParameter();
+ parameter.setName("liangfei");
+ parameter.setEmail("liangfei@liang.fei");
+ parameter.setAge(50);
+ parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));
+ parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));
+ validationService.save(parameter);
+
+ // Save Error
+ try {
+ parameter = new ValidationParameter();
+ validationService.save(parameter);
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("ConstraintViolation"));
+ }
+
+ // Delete OK
+ validationService.delete(2, "abc");
+
+ // Delete Error
+ try {
+ validationService.delete(0, "abc");
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("ConstraintViolation"));
+ }
+ try {
+ validationService.delete(2, null);
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("ConstraintViolation"));
+ }
+ try {
+ validationService.delete(0, null);
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("ConstraintViolation"));
+ }
+ } finally {
+ reference.destroy();
+ }
+ } finally {
+ service.unexport();
+ }
+ }
+
+ @Test
+ public void testGenericValidation() {
+ ServiceConfig<ValidationService> service = new ServiceConfig<ValidationService>();
+ service.setApplication(new ApplicationConfig("validation-provider"));
+ service.setRegistry(new RegistryConfig("N/A"));
+ service.setProtocol(new ProtocolConfig("dubbo", 29582));
+ service.setInterface(ValidationService.class.getName());
+ service.setRef(new ValidationServiceImpl());
+ service.setValidation(String.valueOf(true));
+ service.export();
+ try {
+ ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
+ reference.setApplication(new ApplicationConfig("validation-consumer"));
+ reference.setInterface(ValidationService.class.getName());
+ reference.setUrl("dubbo://127.0.0.1:29582?scope=remote&validation=true");
+ reference.setGeneric(true);
+ GenericService validationService = reference.get();
+ try {
+ // Save OK
+ Map<String, Object> parameter = new HashMap<String, Object>();
+ parameter.put("name", "liangfei");
+ parameter.put("Email", "liangfei@liang.fei");
+ parameter.put("Age", 50);
+ parameter.put("LoginDate", new Date(System.currentTimeMillis() - 1000000));
+ parameter.put("ExpiryDate", new Date(System.currentTimeMillis() + 1000000));
+ validationService.$invoke("save", new String[] {ValidationParameter.class.getName()}, new Object[] {parameter});
+
+ // Save Error
+ try {
+ parameter = new HashMap<String, Object>();
+ validationService.$invoke("save", new String[] {ValidationParameter.class.getName()}, new Object[] {parameter});
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("ConstraintViolation"));
+ }
+
+ // Delete OK
+ validationService.$invoke("delete", new String[] {long.class.getName(), String.class.getName()}, new Object[] {2, "abc"});
+
+ // Delete Error
+ try {
+ validationService.$invoke("delete", new String[] {long.class.getName(), String.class.getName()}, new Object[] {0, "abc"});
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("ConstraintViolation"));
+ }
+ try {
+ validationService.$invoke("delete", new String[] {long.class.getName(), String.class.getName()}, new Object[] {2, null});
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("ConstraintViolation"));
+ }
+ try {
+ validationService.$invoke("delete", new String[] {long.class.getName(), String.class.getName()}, new Object[] {0, null});
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("ConstraintViolation"));
+ }
+ } finally {
+ reference.destroy();
+ }
+ } finally {
+ service.unexport();
+ }
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..6dd0bbd
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+mockregistry=com.alibaba.dubbo.config.support.MockRegistryFactory
diff --git a/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..b95dc2e
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+mockprotocol=com.alibaba.dubbo.config.support.MockProtocol
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/resources/log4j.xml b/dubbo-config/dubbo-config-api/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/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-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml
new file mode 100644
index 0000000..2fa7fef
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/pom.xml
@@ -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.
+-->
+<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-config</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-config-spring</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The spring config module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-config-api</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-rpc-injvm</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>wagon-maven-plugin</artifactId>
+ <version>1.0-beta-3</version>
+ <executions>
+ <execution>
+ <id>upload-schema</id>
+ <phase>deploy</phase>
+ <goals>
+ <goal>upload-single</goal>
+ </goals>
+ <configuration>
+ <fromFile>${basedir}/src/main/resources/META-INF/dubbo.xsd</fromFile>
+ <url>dav:http://code.alibabatech.com/schema/dubbo/</url>
+ <serverId>opensesame.releases.account</serverId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/AnnotationBean.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/AnnotationBean.java
new file mode 100644
index 0000000..e81f58f
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/AnnotationBean.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.config.AbstractConfig;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ConsumerConfig;
+import com.alibaba.dubbo.config.ModuleConfig;
+import com.alibaba.dubbo.config.MonitorConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ProviderConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.ServiceConfig;
+import com.alibaba.dubbo.config.annotation.Reference;
+import com.alibaba.dubbo.config.annotation.Service;
+
+/**
+ * AnnotationBean
+ *
+ * @author william.liangf
+ */
+public class AnnotationBean extends AbstractConfig implements DisposableBean, BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware {
+
+ private static final long serialVersionUID = -7582802454287589552L;
+
+ private static final Logger logger = LoggerFactory.getLogger(Logger.class);
+
+ private String annotationPackage;
+
+ private String[] annotationPackages;
+
+ private final Set<ServiceConfig<?>> serviceConfigs = new ConcurrentHashSet<ServiceConfig<?>>();
+
+ private final ConcurrentMap<String, ReferenceBean<?>> referenceConfigs = new ConcurrentHashMap<String, ReferenceBean<?>>();
+
+ public String getPackage() {
+ return annotationPackage;
+ }
+
+ public void setPackage(String annotationPackage) {
+ this.annotationPackage = annotationPackage;
+ this.annotationPackages = (annotationPackage == null || annotationPackage.length() == 0) ? null
+ : Constants.COMMA_SPLIT_PATTERN.split(annotationPackage);
+ }
+
+ private ApplicationContext applicationContext;
+
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
+ throws BeansException {
+ if (annotationPackage == null || annotationPackage.length() == 0) {
+ return;
+ }
+ if (beanFactory instanceof BeanDefinitionRegistry) {
+ try {
+ // init scanner
+ Class<?> scannerClass = ReflectUtils.forName("org.springframework.context.annotation.ClassPathBeanDefinitionScanner");
+ Object scanner = scannerClass.getConstructor(new Class<?>[] {BeanDefinitionRegistry.class, boolean.class}).newInstance(new Object[] {(BeanDefinitionRegistry) beanFactory, true});
+ // add filter
+ Class<?> filterClass = ReflectUtils.forName("org.springframework.core.type.filter.AnnotationTypeFilter");
+ Object filter = filterClass.getConstructor(Class.class).newInstance(Service.class);
+ Method addIncludeFilter = scannerClass.getMethod("addIncludeFilter", ReflectUtils.forName("org.springframework.core.type.filter.TypeFilter"));
+ addIncludeFilter.invoke(scanner, filter);
+ // scan packages
+ String[] packages = Constants.COMMA_SPLIT_PATTERN.split(annotationPackage);
+ Method scan = scannerClass.getMethod("scan", new Class<?>[]{String[].class});
+ scan.invoke(scanner, new Object[] {packages});
+ } catch (Throwable e) {
+ // spring 2.0
+ }
+ }
+ }
+
+ public void destroy() throws Exception {
+ for (ServiceConfig<?> serviceConfig : serviceConfigs) {
+ try {
+ serviceConfig.unexport();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ for (ReferenceConfig<?> referenceConfig : referenceConfigs.values()) {
+ try {
+ referenceConfig.destroy();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (! isMatchPackage(bean)) {
+ return bean;
+ }
+ Service service = bean.getClass().getAnnotation(Service.class);
+ if (service != null) {
+ ServiceBean<Object> serviceConfig = new ServiceBean<Object>(service);
+ if (void.class.equals(service.interfaceClass())
+ && "".equals(service.interfaceName())) {
+ if (bean.getClass().getInterfaces().length > 0) {
+ serviceConfig.setInterface(bean.getClass().getInterfaces()[0]);
+ } else {
+ throw new IllegalStateException("Failed to export remote service class " + bean.getClass().getName() + ", cause: The @Service undefined interfaceClass or interfaceName, and the service class unimplemented any interfaces.");
+ }
+ }
+ if (applicationContext != null) {
+ serviceConfig.setApplicationContext(applicationContext);
+ if (service.registry() != null && service.registry().length > 0) {
+ List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
+ for (String registryId : service.registry()) {
+ if (registryId != null && registryId.length() > 0) {
+ registryConfigs.add((RegistryConfig)applicationContext.getBean(registryId, RegistryConfig.class));
+ }
+ }
+ serviceConfig.setRegistries(registryConfigs);
+ }
+ if (service.provider() != null && service.provider().length() > 0) {
+ serviceConfig.setProvider((ProviderConfig)applicationContext.getBean(service.provider(),ProviderConfig.class));
+ }
+ if (service.monitor() != null && service.monitor().length() > 0) {
+ serviceConfig.setMonitor((MonitorConfig)applicationContext.getBean(service.monitor(), MonitorConfig.class));
+ }
+ if (service.application() != null && service.application().length() > 0) {
+ serviceConfig.setApplication((ApplicationConfig)applicationContext.getBean(service.application(), ApplicationConfig.class));
+ }
+ if (service.module() != null && service.module().length() > 0) {
+ serviceConfig.setModule((ModuleConfig)applicationContext.getBean(service.module(), ModuleConfig.class));
+ }
+ if (service.provider() != null && service.provider().length() > 0) {
+ serviceConfig.setProvider((ProviderConfig)applicationContext.getBean(service.provider(), ProviderConfig.class));
+ } else {
+
+ }
+ if (service.protocol() != null && service.protocol().length > 0) {
+ List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
+ for (String protocolId : service.registry()) {
+ if (protocolId != null && protocolId.length() > 0) {
+ protocolConfigs.add((ProtocolConfig)applicationContext.getBean(protocolId, ProtocolConfig.class));
+ }
+ }
+ serviceConfig.setProtocols(protocolConfigs);
+ }
+ try {
+ serviceConfig.afterPropertiesSet();
+ } catch (RuntimeException e) {
+ throw (RuntimeException) e;
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ serviceConfig.setRef(bean);
+ serviceConfigs.add(serviceConfig);
+ serviceConfig.export();
+ }
+ return bean;
+ }
+
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (! isMatchPackage(bean)) {
+ return bean;
+ }
+ Method[] methods = bean.getClass().getMethods();
+ for (Method method : methods) {
+ String name = method.getName();
+ if (name.length() > 3 && name.startsWith("set")
+ && method.getParameterTypes().length == 1
+ && Modifier.isPublic(method.getModifiers())
+ && ! Modifier.isStatic(method.getModifiers())) {
+ try {
+ method.invoke(bean, new Object[] { refer(method.getAnnotation(Reference.class), method.getParameterTypes()[0]) });
+ } catch (Throwable e) {
+ throw new IllegalStateException("Failed to init remote service reference at method " + name + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e);
+ }
+ }
+ }
+ Field[] fields = bean.getClass().getDeclaredFields();
+ for (Field field : fields) {
+ try {
+ if (! field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ field.set(bean, refer(field.getAnnotation(Reference.class), field.getType()));
+ } catch (Throwable e) {
+ throw new IllegalStateException("Failed to init remote service reference at filed " + field.getName() + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e);
+ }
+ }
+ return bean;
+ }
+
+ private Object refer(Reference reference, Class<?> referenceClass) { //method.getParameterTypes()[0]
+ if (reference != null) {
+ String interfaceName;
+ if (! "".equals(reference.interfaceName())) {
+ interfaceName = reference.interfaceName();
+ } else if (! void.class.equals(reference.interfaceClass())) {
+ interfaceName = reference.interfaceClass().getName();
+ } else if (referenceClass.isInterface()) {
+ interfaceName = referenceClass.getName();
+ } else {
+ throw new IllegalStateException("The @Reference undefined interfaceClass or interfaceName, and the property type " + referenceClass.getName() + " is not a interface.");
+ }
+ String key = reference.group() + "/" + interfaceName + ":" + reference.version();
+ ReferenceBean<?> referenceConfig = referenceConfigs.get(key);
+ if (referenceConfig == null) {
+ referenceConfig = new ReferenceBean<Object>(reference);
+ if (void.class.equals(reference.interfaceClass())
+ && "".equals(reference.interfaceName())
+ && referenceClass.isInterface()) {
+ referenceConfig.setInterface(referenceClass);
+ }
+ if (applicationContext != null) {
+ referenceConfig.setApplicationContext(applicationContext);
+ if (reference.registry() != null && reference.registry().length > 0) {
+ List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
+ for (String registryId : reference.registry()) {
+ if (registryId != null && registryId.length() > 0) {
+ registryConfigs.add((RegistryConfig)applicationContext.getBean(registryId, RegistryConfig.class));
+ }
+ }
+ referenceConfig.setRegistries(registryConfigs);
+ }
+ if (reference.consumer() != null && reference.consumer().length() > 0) {
+ referenceConfig.setConsumer((ConsumerConfig)applicationContext.getBean(reference.consumer(), ConsumerConfig.class));
+ }
+ if (reference.monitor() != null && reference.monitor().length() > 0) {
+ referenceConfig.setMonitor((MonitorConfig)applicationContext.getBean(reference.monitor(), MonitorConfig.class));
+ }
+ if (reference.application() != null && reference.application().length() > 0) {
+ referenceConfig.setApplication((ApplicationConfig)applicationContext.getBean(reference.application(), ApplicationConfig.class));
+ }
+ if (reference.module() != null && reference.module().length() > 0) {
+ referenceConfig.setModule((ModuleConfig)applicationContext.getBean(reference.module(), ModuleConfig.class));
+ }
+ if (reference.consumer() != null && reference.consumer().length() > 0) {
+ referenceConfig.setConsumer((ConsumerConfig)applicationContext.getBean(reference.consumer(), ConsumerConfig.class));
+ }
+ try {
+ referenceConfig.afterPropertiesSet();
+ } catch (RuntimeException e) {
+ throw (RuntimeException) e;
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ referenceConfigs.putIfAbsent(key, referenceConfig);
+ referenceConfig = referenceConfigs.get(key);
+ }
+ return referenceConfig.get();
+ }
+ return null;
+ }
+
+ private boolean isMatchPackage(Object bean) {
+ if (annotationPackages == null || annotationPackages.length == 0) {
+ return true;
+ }
+ String beanClassName = bean.getClass().getName();
+ for (String pkg : annotationPackages) {
+ if (beanClassName.startsWith(pkg)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.java
new file mode 100644
index 0000000..a72f753
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.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.config.spring;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.DisposableBean;
+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.ModuleConfig;
+import com.alibaba.dubbo.config.MonitorConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.annotation.Reference;
+import com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory;
+import com.alibaba.dubbo.config.support.Parameter;
+
+/**
+ * ReferenceFactoryBean
+ *
+ * @author william.liangf
+ */
+public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
+
+ private static final long serialVersionUID = 213195494150089726L;
+
+ private transient ApplicationContext applicationContext;
+
+ public ReferenceBean() {
+ super();
+ }
+
+ public ReferenceBean(Reference reference) {
+ super(reference);
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ SpringExtensionFactory.addApplicationContext(applicationContext);
+ }
+
+ public Object getObject() throws Exception {
+ return get();
+ }
+
+ public Class<?> getObjectType() {
+ return getInterfaceClass();
+ }
+
+ @Parameter(excluded = true)
+ public boolean isSingleton() {
+ return true;
+ }
+
+ @SuppressWarnings({ "unchecked"})
+ public void afterPropertiesSet() throws Exception {
+ if (getConsumer() == null) {
+ Map<String, ConsumerConfig> consumerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class, false, false);
+ if (consumerConfigMap != null && consumerConfigMap.size() > 0) {
+ ConsumerConfig consumerConfig = null;
+ for (ConsumerConfig config : consumerConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ 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 : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
+ if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
+ ApplicationConfig applicationConfig = null;
+ for (ApplicationConfig config : applicationConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ if (applicationConfig != null) {
+ throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
+ }
+ applicationConfig = config;
+ }
+ }
+ if (applicationConfig != null) {
+ setApplication(applicationConfig);
+ }
+ }
+ }
+ if (getModule() == null
+ && (getConsumer() == null || getConsumer().getModule() == null)) {
+ Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
+ if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
+ ModuleConfig moduleConfig = null;
+ for (ModuleConfig config : moduleConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ if (moduleConfig != null) {
+ throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
+ }
+ moduleConfig = config;
+ }
+ }
+ if (moduleConfig != null) {
+ setModule(moduleConfig);
+ }
+ }
+ }
+ 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 : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
+ if (registryConfigMap != null && registryConfigMap.size() > 0) {
+ List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
+ for (RegistryConfig config : registryConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ registryConfigs.add(config);
+ }
+ }
+ if (registryConfigs != null && registryConfigs.size() > 0) {
+ super.setRegistries(registryConfigs);
+ }
+ }
+ }
+ if (getMonitor() == null
+ && (getConsumer() == null || getConsumer().getMonitor() == null)
+ && (getApplication() == null || getApplication().getMonitor() == null)) {
+ Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
+ if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
+ MonitorConfig monitorConfig = null;
+ for (MonitorConfig config : monitorConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ if (monitorConfig != null) {
+ throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
+ }
+ monitorConfig = config;
+ }
+ }
+ if (monitorConfig != null) {
+ 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/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java
new file mode 100644
index 0000000..4908142
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.DisposableBean;
+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 org.springframework.context.support.AbstractApplicationContext;
+
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ModuleConfig;
+import com.alibaba.dubbo.config.MonitorConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ProviderConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.ServiceConfig;
+import com.alibaba.dubbo.config.annotation.Service;
+import com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory;
+
+/**
+ * ServiceFactoryBean
+ *
+ * @author william.liangf
+ */
+public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, 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 ServiceBean() {
+ super();
+ }
+
+ public ServiceBean(Service service) {
+ super(service);
+ }
+
+ public static ApplicationContext getSpringContext() {
+ return SPRING_CONTEXT;
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ SpringExtensionFactory.addApplicationContext(applicationContext);
+ if (applicationContext != null) {
+ SPRING_CONTEXT = applicationContext;
+ try {
+ Method method = applicationContext.getClass().getMethod("addApplicationListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
+ method.invoke(applicationContext, new Object[] {this});
+ supportedApplicationListener = true;
+ } catch (Throwable t) {
+ if (applicationContext instanceof AbstractApplicationContext) {
+ try {
+ Method method = AbstractApplicationContext.class.getDeclaredMethod("addListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
+ if (! method.isAccessible()) {
+ method.setAccessible(true);
+ }
+ method.invoke(applicationContext, new Object[] {this});
+ supportedApplicationListener = true;
+ } catch (Throwable t2) {
+ }
+ }
+ }
+ }
+ }
+
+ 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", "deprecation" })
+ public void afterPropertiesSet() throws Exception {
+ if (getProvider() == null) {
+ Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
+ if (providerConfigMap != null && providerConfigMap.size() > 0) {
+ Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
+ if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
+ && providerConfigMap.size() > 1) { // 兼容旧版本
+ List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
+ for (ProviderConfig config : providerConfigMap.values()) {
+ if (config.isDefault() != null && config.isDefault().booleanValue()) {
+ providerConfigs.add(config);
+ }
+ }
+ if (providerConfigs.size() > 0) {
+ setProviders(providerConfigs);
+ }
+ } else {
+ ProviderConfig providerConfig = null;
+ for (ProviderConfig config : providerConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ if (providerConfig != null) {
+ throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
+ }
+ providerConfig = config;
+ }
+ }
+ if (providerConfig != null) {
+ setProvider(providerConfig);
+ }
+ }
+ }
+ }
+ if (getApplication() == null
+ && (getProvider() == null || getProvider().getApplication() == null)) {
+ Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
+ if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
+ ApplicationConfig applicationConfig = null;
+ for (ApplicationConfig config : applicationConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ if (applicationConfig != null) {
+ throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
+ }
+ applicationConfig = config;
+ }
+ }
+ if (applicationConfig != null) {
+ setApplication(applicationConfig);
+ }
+ }
+ }
+ if (getModule() == null
+ && (getProvider() == null || getProvider().getModule() == null)) {
+ Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
+ if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
+ ModuleConfig moduleConfig = null;
+ for (ModuleConfig config : moduleConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ if (moduleConfig != null) {
+ throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
+ }
+ moduleConfig = config;
+ }
+ }
+ if (moduleConfig != null) {
+ setModule(moduleConfig);
+ }
+ }
+ }
+ 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 : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
+ if (registryConfigMap != null && registryConfigMap.size() > 0) {
+ List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
+ for (RegistryConfig config : registryConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ registryConfigs.add(config);
+ }
+ }
+ if (registryConfigs != null && registryConfigs.size() > 0) {
+ super.setRegistries(registryConfigs);
+ }
+ }
+ }
+ if (getMonitor() == null
+ && (getProvider() == null || getProvider().getMonitor() == null)
+ && (getApplication() == null || getApplication().getMonitor() == null)) {
+ Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
+ if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
+ MonitorConfig monitorConfig = null;
+ for (MonitorConfig config : monitorConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ if (monitorConfig != null) {
+ throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
+ }
+ monitorConfig = config;
+ }
+ }
+ if (monitorConfig != null) {
+ setMonitor(monitorConfig);
+ }
+ }
+ }
+ if ((getProtocols() == null || getProtocols().size() == 0)
+ && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
+ Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
+ if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
+ List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
+ for (ProtocolConfig config : protocolConfigMap.values()) {
+ if (config.isDefault() == null || config.isDefault().booleanValue()) {
+ protocolConfigs.add(config);
+ }
+ }
+ if (protocolConfigs != null && protocolConfigs.size() > 0) {
+ super.setProtocols(protocolConfigs);
+ }
+ }
+ }
+ 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();
+ }
+ }
+
+ public void destroy() throws Exception {
+ unexport();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/extension/SpringExtensionFactory.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/extension/SpringExtensionFactory.java
new file mode 100644
index 0000000..a060a56
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/extension/SpringExtensionFactory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.extension;
+
+import java.util.Set;
+
+import org.springframework.context.ApplicationContext;
+
+import com.alibaba.dubbo.common.extension.ExtensionFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+
+/**
+ * SpringExtensionFactory
+ *
+ * @author william.liangf
+ */
+public class SpringExtensionFactory implements ExtensionFactory {
+
+ private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>();
+
+ public static void addApplicationContext(ApplicationContext context) {
+ contexts.add(context);
+ }
+
+ public static void removeApplicationContext(ApplicationContext context) {
+ contexts.remove(context);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getExtension(Class<T> type, String name) {
+ for (ApplicationContext context : contexts) {
+ if (context.containsBean(name)) {
+ Object bean = context.getBean(name);
+ if (type.isInstance(bean)) {
+ return (T) bean;
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
new file mode 100644
index 0000000..e449da2
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.schema;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.support.ManagedMap;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.config.ArgumentConfig;
+import com.alibaba.dubbo.config.MethodConfig;
+import com.alibaba.dubbo.config.MonitorConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ProviderConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.spring.ReferenceBean;
+import com.alibaba.dubbo.config.spring.ServiceBean;
+import com.alibaba.dubbo.rpc.Protocol;
+
+/**
+ * AbstractBeanDefinitionParser
+ *
+ * @author william.liangf
+ */
+public class DubboBeanDefinitionParser implements BeanDefinitionParser {
+
+ private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class);
+
+ private final Class<?> beanClass;
+
+ private final boolean required;
+
+ public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
+ this.beanClass = beanClass;
+ this.required = required;
+ }
+
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ return parse(element, parserContext, beanClass, required);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
+ RootBeanDefinition beanDefinition = new RootBeanDefinition();
+ beanDefinition.setBeanClass(beanClass);
+ beanDefinition.setLazyInit(false);
+ String id = element.getAttribute("id");
+ if ((id == null || id.length() == 0) && required) {
+ String generatedBeanName = element.getAttribute("name");
+ if (generatedBeanName == null || generatedBeanName.length() == 0) {
+ if (ProtocolConfig.class.equals(beanClass)) {
+ generatedBeanName = "dubbo";
+ } else {
+ generatedBeanName = element.getAttribute("interface");
+ }
+ }
+ if (generatedBeanName == null || generatedBeanName.length() == 0) {
+ generatedBeanName = beanClass.getName();
+ }
+ id = generatedBeanName;
+ int counter = 2;
+ while(parserContext.getRegistry().containsBeanDefinition(id)) {
+ id = generatedBeanName + (counter ++);
+ }
+ }
+ if (id != null && id.length() > 0) {
+ if (parserContext.getRegistry().containsBeanDefinition(id)) {
+ throw new IllegalStateException("Duplicate spring bean id " + id);
+ }
+ parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
+ beanDefinition.getPropertyValues().addPropertyValue("id", id);
+ }
+ if (ProtocolConfig.class.equals(beanClass)) {
+ for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
+ BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
+ PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
+ if (property != null) {
+ Object value = property.getValue();
+ if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
+ definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
+ }
+ }
+ }
+ } else if (ServiceBean.class.equals(beanClass)) {
+ String className = element.getAttribute("class");
+ if(className != null && className.length() > 0) {
+ RootBeanDefinition classDefinition = new RootBeanDefinition();
+ classDefinition.setBeanClass(ReflectUtils.forName(className));
+ classDefinition.setLazyInit(false);
+ parseProperties(element.getChildNodes(), classDefinition);
+ beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
+ }
+ } else if (ProviderConfig.class.equals(beanClass)) {
+ parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
+ } else if (ProviderConfig.class.equals(beanClass)) {
+ parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
+ }
+ Set<String> props = new HashSet<String>();
+ ManagedMap parameters = null;
+ for (Method setter : beanClass.getMethods()) {
+ String name = setter.getName();
+ if (name.length() > 3 && name.startsWith("set")
+ && Modifier.isPublic(setter.getModifiers())
+ && setter.getParameterTypes().length == 1) {
+ Class<?> type = setter.getParameterTypes()[0];
+ String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
+ props.add(property);
+ Method getter = null;
+ try {
+ getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
+ } catch (NoSuchMethodException e) {
+ try {
+ getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
+ } catch (NoSuchMethodException e2) {
+ }
+ }
+ if (getter == null
+ || ! Modifier.isPublic(getter.getModifiers())
+ || ! type.equals(getter.getReturnType())) {
+ continue;
+ }
+ if ("parameters".equals(property)) {
+ parameters = parseParameters(element.getChildNodes(), beanDefinition);
+ } else if ("methods".equals(property)) {
+ parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
+ } else if ("arguments".equals(property)) {
+ parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
+ } else {
+ String value = element.getAttribute(property);
+ if (value != null) {
+ value = value.trim();
+ if (value.length() > 0) {
+ if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
+ RegistryConfig registryConfig = new RegistryConfig();
+ registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
+ beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
+ } else if ("registry".equals(property) && value.indexOf(',') != -1) {
+ parseMultiRef("registries", value, beanDefinition, parserContext);
+ } else if ("provider".equals(property) && value.indexOf(',') != -1) {
+ parseMultiRef("providers", value, beanDefinition, parserContext);
+ } else if ("protocol".equals(property) && value.indexOf(',') != -1) {
+ parseMultiRef("protocols", value, beanDefinition, parserContext);
+ } else {
+ Object reference;
+ if (isPrimitive(type)) {
+ if ("async".equals(property) && "false".equals(value)
+ || "timeout".equals(property) && "0".equals(value)
+ || "delay".equals(property) && "0".equals(value)
+ || "version".equals(property) && "0.0.0".equals(value)
+ || "stat".equals(property) && "-1".equals(value)
+ || "reliable".equals(property) && "false".equals(value)) {
+ // 兼容旧版本xsd中的default值
+ value = null;
+ }
+ reference = value;
+ } else if ("protocol".equals(property)
+ && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)
+ && (! parserContext.getRegistry().containsBeanDefinition(value)
+ || ! ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
+ if ("dubbo:provider".equals(element.getTagName())) {
+ logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
+ }
+ // 兼容旧版本配置
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setName(value);
+ reference = protocol;
+ } else if ("monitor".equals(property)
+ && (! parserContext.getRegistry().containsBeanDefinition(value)
+ || ! MonitorConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
+ // 兼容旧版本配置
+ reference = convertMonitor(value);
+ } else if ("onreturn".equals(property)) {
+ int index = value.lastIndexOf(".");
+ String returnRef = value.substring(0, index);
+ String returnMethod = value.substring(index + 1);
+ reference = new RuntimeBeanReference(returnRef);
+ beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
+ } else if ("onthrow".equals(property)) {
+ int index = value.lastIndexOf(".");
+ String throwRef = value.substring(0, index);
+ String throwMethod = value.substring(index + 1);
+ reference = new RuntimeBeanReference(throwRef);
+ beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
+ } else {
+ if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
+ BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
+ if (! refBean.isSingleton()) {
+ throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value+ "\" scope=\"singleton\" ...>");
+ }
+ }
+ reference = new RuntimeBeanReference(value);
+ }
+ beanDefinition.getPropertyValues().addPropertyValue(property, reference);
+ }
+ }
+ }
+ }
+ }
+ }
+ NamedNodeMap attributes = element.getAttributes();
+ int len = attributes.getLength();
+ for (int i = 0; i < len; i++) {
+ Node node = attributes.item(i);
+ String name = node.getLocalName();
+ if (! props.contains(name)) {
+ if (parameters == null) {
+ parameters = new ManagedMap();
+ }
+ String value = node.getNodeValue();
+ parameters.put(name, new TypedStringValue(value, String.class));
+ }
+ }
+ if (parameters != null) {
+ beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
+ }
+ return beanDefinition;
+ }
+
+ private static final Pattern GROUP_AND_VERION = Pattern.compile("^[\\-.0-9_a-zA-Z]+(\\:[\\-.0-9_a-zA-Z]+)?$");
+
+ protected static MonitorConfig convertMonitor(String monitor) {
+ if (monitor == null || monitor.length() == 0) {
+ return null;
+ }
+ if (GROUP_AND_VERION.matcher(monitor).matches()) {
+ String group;
+ String version;
+ int i = monitor.indexOf(':');
+ if (i > 0) {
+ group = monitor.substring(0, i);
+ version = monitor.substring(i + 1);
+ } else {
+ group = monitor;
+ version = null;
+ }
+ MonitorConfig monitorConfig = new MonitorConfig();
+ monitorConfig.setGroup(group);
+ monitorConfig.setVersion(version);
+ return monitorConfig;
+ }
+ return null;
+ }
+
+ private static boolean isPrimitive(Class<?> cls) {
+ return cls.isPrimitive() || cls == Boolean.class || cls == Byte.class
+ || cls == Character.class || cls == Short.class || cls == Integer.class
+ || cls == Long.class || cls == Float.class || cls == Double.class
+ || cls == String.class || cls == Date.class || cls == Class.class;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition,
+ ParserContext parserContext) {
+ String[] values = value.split("\\s*[,]+\\s*");
+ ManagedList list = null;
+ for (int i = 0; i < values.length; i++) {
+ String v = values[i];
+ if (v != null && v.length() > 0) {
+ if (list == null) {
+ list = new ManagedList();
+ }
+ list.add(new RuntimeBeanReference(v));
+ }
+ }
+ beanDefinition.getPropertyValues().addPropertyValue(property, list);
+ }
+
+ private static void parseNested(Element element, ParserContext parserContext, Class<?> beanClass, boolean required, String tag, String property, String ref, BeanDefinition beanDefinition) {
+ NodeList nodeList = element.getChildNodes();
+ if (nodeList != null && nodeList.getLength() > 0) {
+ boolean first = true;
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node instanceof Element) {
+ if (tag.equals(node.getNodeName())
+ || tag.equals(node.getLocalName())) {
+ if (first) {
+ first = false;
+ String isDefault = element.getAttribute("default");
+ if (isDefault == null || isDefault.length() == 0) {
+ beanDefinition.getPropertyValues().addPropertyValue("default", "false");
+ }
+ }
+ BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required);
+ if (subDefinition != null && ref != null && ref.length() > 0) {
+ subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static void parseProperties(NodeList nodeList, RootBeanDefinition beanDefinition) {
+ if (nodeList != null && nodeList.getLength() > 0) {
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node instanceof Element) {
+ if ("property".equals(node.getNodeName())
+ || "property".equals(node.getLocalName())) {
+ String name = ((Element) node).getAttribute("name");
+ if (name != null && name.length() > 0) {
+ String value = ((Element) node).getAttribute("value");
+ String ref = ((Element) node).getAttribute("ref");
+ if (value != null && value.length() > 0) {
+ beanDefinition.getPropertyValues().addPropertyValue(name, value);
+ } else if (ref != null && ref.length() > 0) {
+ beanDefinition.getPropertyValues().addPropertyValue(name, new RuntimeBeanReference(ref));
+ } else {
+ throw new UnsupportedOperationException("Unsupported <property name=\"" + name + "\"> sub tag, Only supported <property name=\"" + name + "\" ref=\"...\" /> or <property name=\"" + name + "\" value=\"...\" />");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition) {
+ if (nodeList != null && nodeList.getLength() > 0) {
+ ManagedMap parameters = null;
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node instanceof Element) {
+ if ("parameter".equals(node.getNodeName())
+ || "parameter".equals(node.getLocalName())) {
+ if (parameters == null) {
+ parameters = new ManagedMap();
+ }
+ String key = ((Element) node).getAttribute("key");
+ String value = ((Element) node).getAttribute("value");
+ boolean hide = "true".equals(((Element) node).getAttribute("hide"));
+ if (hide) {
+ key = Constants.HIDE_KEY_PREFIX + key;
+ }
+ parameters.put(key, new TypedStringValue(value, String.class));
+ }
+ }
+ }
+ return parameters;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
+ ParserContext parserContext) {
+ if (nodeList != null && nodeList.getLength() > 0) {
+ ManagedList methods = null;
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node instanceof Element) {
+ Element element = (Element) node;
+ if ("method".equals(node.getNodeName()) || "method".equals(node.getLocalName())) {
+ String methodName = element.getAttribute("name");
+ if (methodName == null || methodName.length() == 0) {
+ throw new IllegalStateException("<dubbo:method> name attribute == null");
+ }
+ if (methods == null) {
+ methods = new ManagedList();
+ }
+ BeanDefinition methodBeanDefinition = parse(((Element) node),
+ parserContext, MethodConfig.class, false);
+ String name = id + "." + methodName;
+ BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(
+ methodBeanDefinition, name);
+ methods.add(methodBeanDefinitionHolder);
+ }
+ }
+ }
+ if (methods != null) {
+ beanDefinition.getPropertyValues().addPropertyValue("methods", methods);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void parseArguments(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
+ ParserContext parserContext) {
+ if (nodeList != null && nodeList.getLength() > 0) {
+ ManagedList arguments = null;
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node instanceof Element) {
+ Element element = (Element) node;
+ if ("argument".equals(node.getNodeName()) || "argument".equals(node.getLocalName())) {
+ String argumentIndex = element.getAttribute("index");
+ if (arguments == null) {
+ arguments = new ManagedList();
+ }
+ BeanDefinition argumentBeanDefinition = parse(((Element) node),
+ parserContext, ArgumentConfig.class, false);
+ String name = id + "." + argumentIndex;
+ BeanDefinitionHolder argumentBeanDefinitionHolder = new BeanDefinitionHolder(
+ argumentBeanDefinition, name);
+ arguments.add(argumentBeanDefinitionHolder);
+ }
+ }
+ }
+ if (arguments != null) {
+ beanDefinition.getPropertyValues().addPropertyValue("arguments", arguments);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboNamespaceHandler.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboNamespaceHandler.java
new file mode 100644
index 0000000..4194533
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboNamespaceHandler.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.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.ModuleConfig;
+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.AnnotationBean;
+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("module", new DubboBeanDefinitionParser(ModuleConfig.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));
+ registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/status/DataSourceStatusChecker.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/status/DataSourceStatusChecker.java
new file mode 100644
index 0000000..4388d06
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/status/DataSourceStatusChecker.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.status;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.springframework.context.ApplicationContext;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.config.spring.ServiceBean;
+
+/**
+ * DataSourceStatusChecker
+ *
+ * @author william.liangf
+ */
+@Activate
+public class DataSourceStatusChecker implements StatusChecker {
+
+ private static final Logger logger = LoggerFactory.getLogger(DataSourceStatusChecker.class);
+
+ @SuppressWarnings("unchecked")
+ public Status check() {
+ ApplicationContext context = ServiceBean.getSpringContext();
+ if (context == null) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Map<String, DataSource> dataSources = context.getBeansOfType(DataSource.class, false, false);
+ if (dataSources == null || dataSources.size() == 0) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Status.Level level = Status.Level.OK;
+ StringBuilder buf = new StringBuilder();
+ for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
+ DataSource dataSource = entry.getValue();
+ if (buf.length() > 0) {
+ buf.append(", ");
+ }
+ buf.append(entry.getKey());
+ try {
+ Connection connection = dataSource.getConnection();
+ try {
+ DatabaseMetaData metaData = connection.getMetaData();
+ ResultSet resultSet = metaData.getTypeInfo();
+ try {
+ if (! resultSet.next()) {
+ level = Status.Level.ERROR;
+ }
+ } finally {
+ resultSet.close();
+ }
+ buf.append(metaData.getURL());
+ buf.append("(");
+ buf.append(metaData.getDatabaseProductName());
+ buf.append("-");
+ buf.append(metaData.getDatabaseProductVersion());
+ buf.append(")");
+ } finally {
+ connection.close();
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ return new Status(level, e.getMessage());
+ }
+ }
+ return new Status(level, buf.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/status/SpringStatusChecker.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/status/SpringStatusChecker.java
new file mode 100644
index 0000000..a2265b0
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/status/SpringStatusChecker.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.status;
+
+import java.lang.reflect.Method;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.Lifecycle;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.config.spring.ServiceBean;
+
+/**
+ * SpringStatusChecker
+ *
+ * @author william.liangf
+ */
+@Activate
+public class SpringStatusChecker implements StatusChecker {
+
+ private static final Logger logger = LoggerFactory.getLogger(SpringStatusChecker.class);
+
+ public Status check() {
+ ApplicationContext context = ServiceBean.getSpringContext();
+ if (context == null) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Status.Level level = Status.Level.OK;
+ if (context instanceof Lifecycle) {
+ if (((Lifecycle)context).isRunning()) {
+ level = Status.Level.OK;
+ } else {
+ level = Status.Level.ERROR;
+ }
+ } else {
+ level = Status.Level.UNKNOWN;
+ }
+ StringBuilder buf = new StringBuilder();
+ try {
+ Class<?> cls = context.getClass();
+ Method method = null;
+ while (cls != null && method == null) {
+ try {
+ method = cls.getDeclaredMethod("getConfigLocations", new Class<?>[0]);
+ } catch (NoSuchMethodException t) {
+ cls = cls.getSuperclass();
+ }
+ }
+ if (method != null) {
+ if (! method.isAccessible()) {
+ method.setAccessible(true);
+ }
+ String[] configs = (String[]) method.invoke(context, new Object[0]);
+ if (configs != null && configs.length > 0) {
+ for (String config : configs) {
+ if (buf.length() > 0) {
+ buf.append(",");
+ }
+ buf.append(config);
+ }
+ }
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ return new Status(level, buf.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
new file mode 100644
index 0000000..ea509fe
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
@@ -0,0 +1,1135 @@
+<?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"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xmlns:tool="http://www.springframework.org/schema/tool"
+ targetNamespace="http://code.alibabatech.com/schema/dubbo">
+
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
+ <xsd:import namespace="http://www.springframework.org/schema/beans"/>
+ <xsd:import namespace="http://www.springframework.org/schema/tool"/>
+
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Namespace support for the dubbo services provided by dubbo framework. ]]></xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:complexType name="abstractMethodType">
+ <xsd:attribute name="timeout" type="xsd:string" use="optional" default="0">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The method invoke timeout. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="retries" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The method retry times. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="actives" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The max active requests. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="connections" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The exclusive connections. default share one connection. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="loadbalance" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The method load balance. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="async" type="xsd:string" use="optional" default="false">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The method does async. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="sent" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The async method return await message sent ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="mock" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Use service mock implemention. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="merger" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The multi-group result merger ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="validation" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Use service jsr303 validation, true/false. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="cache" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Use service cache, lru/threadlocal/jcache. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="abstractInterfaceType">
+ <xsd:complexContent>
+ <xsd:extension base="abstractMethodType">
+ <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="module" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service module. ]]></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:attribute name="scope" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Defines the service visibility, choise:[local remote]. default is remote, which can be invoked by network。 ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="abstractReferenceType">
+ <xsd:complexContent>
+ <xsd:extension base="abstractInterfaceType">
+ <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="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[Deprecated. Replace to set scope=local ]]></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:complexType name="abstractServiceType">
+ <xsd:complexContent>
+ <xsd:extension base="abstractInterfaceType">
+ <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="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="export" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ The service is export. ]]>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="weight" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ The service weight. ]]>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="document" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ The service document. ]]>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="dynamic" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ the service registered to the registry is dynamic(true) or static(false). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="token" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service use token. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="accesslog" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service use accesslog. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="executes" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service allow execute requests. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="protocol" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service protocol. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="applicationType">
+ <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="version" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application version. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="owner" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application owner name (email prefix). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="organization" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The organization name. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="architecture" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The architecture. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="environment" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application environment, eg: dev/test/run ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="compiler" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The java code compiler. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="logger" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application logger. ]]></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:attribute name="default" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="moduleType">
+ <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 module name. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="version" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The module version. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="owner" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The module owner name (email prefix). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="organization" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The module organization. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="registry" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The module registry. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="monitor" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The module monitor. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="default" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="registryType">
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:ID">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="address" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry address. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="port" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry default port. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="protocol" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry lookup protocol. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="username" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry username. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="password" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry password. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="transport" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="transporter" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="server" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol server type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol client type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="group" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry group. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="version" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry version. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="timeout" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The request timeout. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="session" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The session timeout. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="file" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry adddress file store. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="wait" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The wait time for shutdown. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="check" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Check registry status on stratup. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="dynamic" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ the service registered to this registry is dynamic(true) or static(false). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="register" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ register service to this registry(true) or not(false). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="subscribe" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ subscribe service to this registry(true) or not(false). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="default" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="monitorType">
+ <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:attribute name="default" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="parameterType">
+ <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:complexType name="methodType">
+ <xsd:complexContent>
+ <xsd:extension base="abstractMethodType">
+ <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:complexType name="argumentType">
+ <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:complexType name="consumerType">
+ <xsd:complexContent>
+ <xsd:extension base="abstractReferenceType">
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:sequence>
+ <xsd:attribute name="default" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="referenceType">
+ <xsd:complexContent>
+ <xsd:extension base="abstractReferenceType">
+ <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:appinfo>
+ <tool:annotation>
+ <tool:expected-type type="java.lang.Class"/>
+ </tool:annotation>
+ </xsd:appinfo>
+ </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:attribute name="protocol" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service protocol. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="protocolType">
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:ID">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="name" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol name. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="host" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service host. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="port" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service port. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="threadpool" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="threads" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="iothreads" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The IO thread pool size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="queues" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool queue size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="accepts" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The accept connection size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="codec" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol codec. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="serialization" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol serialization. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="charset" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol charset. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="payload" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The max payload. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="buffer" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The buffer size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="heartbeat" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The heartbeat interval.(ms) ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="accesslog" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol use accesslog. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="telnet" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol use telnet commands. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="prompt" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol telnet prompt. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="status" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol check status. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="transporter" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="exchanger" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol exchanger type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="dispather" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol dispather type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="networker" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol "networker" type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="server" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol server type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol client type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="path" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol context path. replace to "contextpath". ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="contextpath" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol context path. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="register" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol can be register to registry. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="default" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:complexType>
+
+ <xsd:complexType name="providerType">
+ <xsd:complexContent>
+ <xsd:extension base="abstractServiceType">
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="service" minOccurs="0" maxOccurs="unbounded" />
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:choice>
+ <xsd:attribute name="host" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service host. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="port" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service port. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="threadpool" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="threads" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="iothreads" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The IO thread pool size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="queues" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool queue size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="accepts" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The accept connection size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="codec" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol codec. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="serialization" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol serialization. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="charset" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol charset. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="payload" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The max payload. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="buffer" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The buffer size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="transporter" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="exchanger" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol exchanger type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="dispather" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol dispather type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="networker" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol "networker" type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="server" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol server type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol client type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="telnet" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol use telnet commands. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="prompt" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol telnet prompt. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="status" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol check status. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="path" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol context path. replace to "contextpath". ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="contextpath" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol context path. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="wait" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The provider shutdown wait time. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="default" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="serviceType">
+ <xsd:complexContent>
+ <xsd:extension base="abstractServiceType">
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="method" minOccurs="0" maxOccurs="unbounded" />
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ <xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:choice>
+ <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:appinfo>
+ <tool:annotation>
+ <tool:expected-type type="java.lang.Class"/>
+ </tool:annotation>
+ </xsd:appinfo>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="ref" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service implementation instance bean id. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="class" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service implementation class name. ]]></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="provider" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Deprecated. Replace to protocol. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="annotationType">
+ <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="package" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The scan package. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:element name="annotation" type="annotationType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The annotation config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="application" type="applicationType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="module" type="moduleType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The module config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="registry" type="registryType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="monitor" type="monitorType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The logstat monitor config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="provider" type="providerType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Export service default config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="consumer" type="consumerType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Service reference default config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="protocol" type="protocolType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Service provider config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="service" type="serviceType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="reference" type="referenceType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Reference service config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="method" type="methodType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service method config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="argument" type="argumentType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service argument config ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+ <xsd:element name="parameter" type="parameterType">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service url parameter ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+
+</xsd:schema>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory
new file mode 100644
index 0000000..95935f0
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory
@@ -0,0 +1 @@
+spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..fa52ac2
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+spring=com.alibaba.dubbo.config.spring.status.SpringStatusChecker
+datasource=com.alibaba.dubbo.config.spring.status.DataSourceStatusChecker
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/spring.handlers b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000..33510e6
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/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/dubbo-config-spring/src/main/resources/META-INF/spring.schemas b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000..b520c36
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1 @@
+http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/AbstractRegistryService.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/AbstractRegistryService.java
new file mode 100644
index 0000000..a785c60
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/AbstractRegistryService.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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) {
+ URL deleteURL = null;
+ for (URL u : urls) {
+ if (u.toIdentityString().equals(url.toIdentityString())) {
+ deleteURL = u;
+ break;
+ }
+ }
+ if (deleteURL != null) {
+ urls.remove(deleteURL);
+ }
+ }
+ }
+
+ public void subscribe(String service, URL url, NotifyListener listener) {
+ if (service == null) {
+ throw new IllegalArgumentException("service == null");
+ }
+ if (url == null) {
+ throw new IllegalArgumentException("parameters == null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener == null");
+ }
+ subscribed.put(service, url.getParameters());
+ addListener(service, listener);
+ }
+
+ public void unsubscribe(String service, URL url, NotifyListener listener) {
+ if (service == null) {
+ throw new IllegalArgumentException("service == null");
+ }
+ if (url == null) {
+ throw new IllegalArgumentException("parameters == null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener == null");
+ }
+ subscribed.remove(service);
+ removeListener(service, listener);
+ }
+
+ //consumer 与 provider的 listener可以一起存储,都是根据服务名称共享
+ private void addListener(final String service, final NotifyListener listener){
+ if (listener == null) {
+ return;
+ }
+ List<NotifyListener> listeners = notifyListeners.get(service);
+ if (listeners == null) {
+ notifyListeners.putIfAbsent(service, new CopyOnWriteArrayList<NotifyListener>());
+ listeners = notifyListeners.get(service);
+ }
+ if (listeners != null && !listeners.contains(listener)){
+ listeners.add(listener);
+ }
+ }
+
+ private void removeListener(final String service, final NotifyListener listener){
+ if (listener == null) {
+ return;
+ }
+ List<NotifyListener> listeners = notifyListeners.get(service);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+
+ private void doNotify(String service, List<URL> urls) {
+ notified.put(service, urls);
+ List<NotifyListener> listeners = notifyListeners.get(service);
+ if (listeners != null) {
+ for (NotifyListener listener : listeners) {
+ try {
+ notify(service, urls, listener);
+ } catch (Throwable t) {
+ logger.error("Failed to notify registry event, service: " + service + ", urls: " + urls + ", cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ }
+
+ protected void notify(String service, List<URL> urls, NotifyListener listener) {
+ listener.notify(urls);
+ }
+
+ protected final void forbid(String service) {
+ doNotify(service, new ArrayList<URL>(0));
+ }
+
+ protected final void notify(String service, List<URL> urls) {
+ if (service == null || service.length() == 0
+ || urls == null || urls.size() == 0) {
+ return;
+ }
+ doNotify(service, urls);
+ }
+
+ public Map<String, List<URL>> getRegistered() {
+ return Collections.unmodifiableMap(registered);
+ }
+
+ public List<URL> getRegistered(String service) {
+ return Collections.unmodifiableList(registered.get(service));
+ }
+
+ public Map<String, Map<String, String>> getSubscribed() {
+ return Collections.unmodifiableMap(subscribed);
+ }
+
+ public Map<String, String> getSubscribed(String service) {
+ return subscribed.get(service);
+ }
+
+ public Map<String, List<URL>> getNotified() {
+ return Collections.unmodifiableMap(notified);
+ }
+
+ public List<URL> getNotified(String service) {
+ return Collections.unmodifiableList(notified.get(service));
+ }
+
+ public Map<String, List<NotifyListener>> getListeners() {
+ return Collections.unmodifiableMap(notifyListeners);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/ConfigTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/ConfigTest.java
new file mode 100644
index 0000000..7001acd
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/ConfigTest.java
@@ -0,0 +1,807 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 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.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ConsumerConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ProviderConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.ServiceConfig;
+import com.alibaba.dubbo.config.spring.action.DemoActionByAnnotation;
+import com.alibaba.dubbo.config.spring.action.DemoActionBySetter;
+import com.alibaba.dubbo.config.spring.annotation.consumer.AnnotationAction;
+import com.alibaba.dubbo.config.spring.api.DemoService;
+import com.alibaba.dubbo.config.spring.filter.MockFilter;
+import com.alibaba.dubbo.config.spring.impl.DemoServiceImpl;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+
+
+/**
+ * ConfigTest
+ *
+ * @author william.liangf
+ */
+public class ConfigTest {
+
+ @Test
+ public void testSpringExtensionInject() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/spring-extension-inject.xml");
+ ctx.start();
+ try {
+ MockFilter filter = (MockFilter) ExtensionLoader.getExtensionLoader(Filter.class).getExtension("mymock");
+ assertNotNull(filter.getMockDao());
+ assertNotNull(filter.getProtocol());
+ assertNotNull(filter.getLoadBalance());
+ } finally {
+ ctx.stop();
+ ctx.close();
+ }
+ }
+
+ @Test
+ public void testServiceClass() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/service-class.xml");
+ ctx.start();
+ try {
+ DemoService demoService = refer("dubbo://127.0.0.1:20887");
+ String hello = demoService.sayName("hello");
+ assertEquals("welcome:hello", hello);
+ } finally {
+ ctx.stop();
+ ctx.close();
+ }
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testProviderNestedService() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/provider-nested-service.xml");
+ ctx.start();
+ try {
+ ServiceConfig<DemoService> serviceConfig = (ServiceConfig<DemoService>) ctx.getBean("serviceConfig");
+ assertNotNull(serviceConfig.getProvider());
+ assertEquals(2000, serviceConfig.getProvider().getTimeout().intValue());
+
+ ServiceConfig<DemoService> serviceConfig2 = (ServiceConfig<DemoService>) ctx.getBean("serviceConfig2");
+ assertNotNull(serviceConfig2.getProvider());
+ assertEquals(1000, serviceConfig2.getProvider().getTimeout().intValue());
+ } finally {
+ ctx.stop();
+ ctx.close();
+ }
+ }
+
+ private DemoService refer(String url) {
+ ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
+ reference.setApplication(new ApplicationConfig("consumer"));
+ reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));
+ reference.setInterface(DemoService.class);
+ reference.setUrl(url);
+ return reference.get();
+ }
+
+ @Test
+ public void testToString() {
+ ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
+ reference.setApplication(new ApplicationConfig("consumer"));
+ reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));
+ reference.setInterface(DemoService.class);
+ reference.setUrl("dubbo://127.0.0.1:20881");
+ String str = reference.toString();
+ assertTrue(str.startsWith("<dubbo:reference "));
+ assertTrue(str.contains(" url=\"dubbo://127.0.0.1:20881\" "));
+ assertTrue(str.contains(" interface=\"com.alibaba.dubbo.config.spring.api.DemoService\" "));
+ assertTrue(str.endsWith(" />"));
+ }
+
+ @Test
+ public void testMultiProtocol() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol.xml");
+ ctx.start();
+ try {
+ DemoService demoService = refer("dubbo://127.0.0.1:20881");
+ String hello = demoService.sayName("hello");
+ assertEquals("say:hello", hello);
+ } finally {
+ ctx.stop();
+ ctx.close();
+ }
+ }
+
+ @Test
+ public void testMultiProtocolDefault() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-default.xml");
+ ctx.start();
+ try {
+ DemoService demoService = refer("rmi://127.0.0.1:10991");
+ String hello = demoService.sayName("hello");
+ assertEquals("say:hello", hello);
+ } finally {
+ ctx.stop();
+ ctx.close();
+ }
+ }
+
+ @Test
+ public void testMultiProtocolError() {
+ try {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-error.xml");
+ ctx.start();
+ ctx.stop();
+ ctx.close();
+ } catch (BeanCreationException e) {
+ assertTrue(e.getMessage().contains("Found multi-protocols"));
+ }
+ }
+
+ @Test
+ public void testMultiProtocolRegister() {
+ SimpleRegistryService registryService = new SimpleRegistryService();
+ Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4547, registryService);
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-register.xml");
+ ctx.start();
+ try {
+ List<URL> urls = registryService.getRegistered().get("com.alibaba.dubbo.config.spring.api.DemoService");
+ assertNotNull(urls);
+ assertEquals(1, urls.size());
+ assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20824/com.alibaba.dubbo.config.spring.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.spring.api.DemoService");
+ assertNull(urls1);
+ List<URL> urls2 = registryService2.getRegistered().get("com.alibaba.dubbo.config.spring.api.DemoService");
+ assertNotNull(urls2);
+ assertEquals(1, urls2.size());
+ assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20880/com.alibaba.dubbo.config.spring.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.spring.api.DemoService");
+ assertNull(urls);
+ int i = 0;
+ while ((i ++) < 60 && urls == null) {
+ urls = registryService.getRegistered().get("com.alibaba.dubbo.config.spring.api.DemoService");
+ Thread.sleep(10);
+ }
+ assertNotNull(urls);
+ assertEquals(1, urls.size());
+ assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20883/com.alibaba.dubbo.config.spring.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.spring.api.DemoService");
+ assertNotNull(urls);
+ assertEquals(1, urls.size());
+ assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20883/com.alibaba.dubbo.config.spring.api.DemoService", urls.get(0).toIdentityString());
+ } finally {
+ ctx.stop();
+ ctx.close();
+ exporter.unexport();
+ }
+ }
+
+ @Test
+ public void testRmiTimeout() throws Exception {
+ if (System.getProperty("sun.rmi.transport.tcp.responseTimeout") != null) {
+ System.setProperty("sun.rmi.transport.tcp.responseTimeout", "");
+ }
+ ConsumerConfig consumer = new ConsumerConfig();
+ consumer.setTimeout(1000);
+ assertEquals("1000", System.getProperty("sun.rmi.transport.tcp.responseTimeout"));
+ consumer.setTimeout(2000);
+ assertEquals("1000", System.getProperty("sun.rmi.transport.tcp.responseTimeout"));
+ }
+
+ @Test
+ public void testAutowireAndAOP() throws Exception {
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider.xml");
+ providerContext.start();
+ try {
+ ClassPathXmlApplicationContext byNameContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/aop-autowire-byname.xml");
+ byNameContext.start();
+ try {
+ DemoActionBySetter demoActionBySetter = (DemoActionBySetter) byNameContext.getBean("demoActionBySetter");
+ assertNotNull(demoActionBySetter.getDemoService());
+ assertEquals("aop:say:hello", demoActionBySetter.getDemoService().sayName("hello"));
+ DemoActionByAnnotation demoActionByAnnotation = (DemoActionByAnnotation) byNameContext.getBean("demoActionByAnnotation");
+ assertNotNull(demoActionByAnnotation.getDemoService());
+ assertEquals("aop:say:hello", demoActionByAnnotation.getDemoService().sayName("hello"));
+ } finally {
+ byNameContext.stop();
+ byNameContext.close();
+ }
+ ClassPathXmlApplicationContext byTypeContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/aop-autowire-bytype.xml");
+ byTypeContext.start();
+ try {
+ DemoActionBySetter demoActionBySetter = (DemoActionBySetter) byTypeContext.getBean("demoActionBySetter");
+ assertNotNull(demoActionBySetter.getDemoService());
+ assertEquals("aop:say:hello", demoActionBySetter.getDemoService().sayName("hello"));
+ DemoActionByAnnotation demoActionByAnnotation = (DemoActionByAnnotation) byTypeContext.getBean("demoActionByAnnotation");
+ assertNotNull(demoActionByAnnotation.getDemoService());
+ assertEquals("aop:say:hello", demoActionByAnnotation.getDemoService().sayName("hello"));
+ } finally {
+ byTypeContext.stop();
+ byTypeContext.close();
+ }
+ } finally {
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+ @Test
+ public void testAppendFilter() throws Exception {
+ ProviderConfig provider = new ProviderConfig();
+ provider.setFilter("classloader,monitor");
+ ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
+ service.setFilter("accesslog,trace");
+ service.setProvider(provider);
+ service.setProtocol(new ProtocolConfig("dubbo", 20880));
+ service.setApplication(new ApplicationConfig("provider"));
+ service.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));
+ service.setInterface(DemoService.class);
+ service.setRef(new DemoServiceImpl());
+ try {
+ service.export();
+ List<URL> urls = service.toUrls();
+ assertNotNull(urls);
+ assertEquals(1, urls.size());
+ assertEquals("classloader,monitor,accesslog,trace", urls.get(0).getParameter("service.filter"));
+
+ ConsumerConfig consumer = new ConsumerConfig();
+ consumer.setFilter("classloader,monitor");
+ ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
+ reference.setFilter("accesslog,trace");
+ reference.setConsumer(consumer);
+ reference.setApplication(new ApplicationConfig("consumer"));
+ reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));
+ reference.setInterface(DemoService.class);
+ reference.setUrl("dubbo://" + NetUtils.getLocalHost() + ":20880?" + DemoService.class.getName() + "?check=false");
+ try {
+ reference.get();
+ urls = reference.toUrls();
+ assertNotNull(urls);
+ assertEquals(1, urls.size());
+ assertEquals("classloader,monitor,accesslog,trace", urls.get(0).getParameter("reference.filter"));
+ } finally {
+ reference.destroy();
+ }
+ } finally {
+ service.unexport();
+ }
+ }
+
+ @Test
+ public void testInitReference() throws Exception {
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider.xml");
+ providerContext.start();
+ try {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/init-reference.xml");
+ ctx.start();
+ try {
+ DemoService demoService = (DemoService)ctx.getBean("demoService");
+ assertEquals("say:world", demoService.sayName("world"));
+ } finally {
+ ctx.stop();
+ ctx.close();
+ }
+ } finally {
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+ // DUBBO-147 通过RpcContext可以获得所有尝试过的Invoker
+ @Test
+ public void test_RpcContext_getUrls() 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-getUrls.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().getUrls().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().getUrls().size());
+ } finally {
+ ctx.stop();
+ ctx.close();
+ }
+ } finally {
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+ // BUG: DUBBO-146 Dubbo序列化失败(如传输对象没有实现Serialiable接口),Provider端也没有异常输出,Consumer端超时出错
+ @Test
+ public void test_returnSerializationFail() throws Exception {
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/demo-provider-UnserializableBox.xml");
+ providerContext.start();
+ try {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/init-reference.xml");
+ ctx.start();
+ try {
+ DemoService demoService = (DemoService)ctx.getBean("demoService");
+ try {
+ demoService.getBox();
+ fail();
+ } catch (RpcException expected) {
+ assertThat(expected.getMessage(), containsString("must implement java.io.Serializable"));
+ }
+ } finally {
+ ctx.stop();
+ ctx.close();
+ }
+ } finally {
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+ @Test
+ public void testXmlOverrideProperties() throws Exception {
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/xml-override-properties.xml");
+ providerContext.start();
+ try {
+ ApplicationConfig application = (ApplicationConfig) providerContext.getBean("application");
+ assertEquals("demo-provider", application.getName());
+ assertEquals("world", application.getOwner());
+
+ RegistryConfig registry = (RegistryConfig) providerContext.getBean("registry");
+ assertEquals("N/A", registry.getAddress());
+
+ ProtocolConfig dubbo = (ProtocolConfig) providerContext.getBean("dubbo");
+ assertEquals(20813, dubbo.getPort().intValue());
+
+ } finally {
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+ @Test
+ public void testApiOverrideProperties() throws Exception {
+ ApplicationConfig application = new ApplicationConfig();
+ application.setName("api-override-properties");
+
+ RegistryConfig registry = new RegistryConfig();
+ registry.setAddress("N/A");
+
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setName("dubbo");
+ protocol.setPort(13123);
+
+ ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
+ service.setInterface(DemoService.class);
+ service.setRef(new DemoServiceImpl());
+ service.setApplication(application);
+ service.setRegistry(registry);
+ service.setProtocol(protocol);
+ service.export();
+
+ try {
+ URL url = service.toUrls().get(0);
+ assertEquals("api-override-properties", url.getParameter("application"));
+ assertEquals("world", url.getParameter("owner"));
+ assertEquals(13123, url.getPort());
+
+ ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
+ reference.setApplication(new ApplicationConfig("consumer"));
+ reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));
+ reference.setInterface(DemoService.class);
+ reference.setUrl("dubbo://127.0.0.1:13123");
+ reference.get();
+ try {
+ url = reference.toUrls().get(0);
+ assertEquals("2000", url.getParameter("timeout"));
+ } finally {
+ reference.destroy();
+ }
+ } finally {
+ service.unexport();
+ }
+ }
+
+ @Test
+ public void testSystemPropertyOverrideProtocol() throws Exception {
+ System.setProperty("dubbo.protocol.port", "20812");
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/override-protocol.xml");
+ providerContext.start();
+ try {
+ ProtocolConfig dubbo = (ProtocolConfig) providerContext.getBean("dubbo");
+ assertEquals(20812, dubbo.getPort().intValue());
+ } finally {
+ System.setProperty("dubbo.protocol.port", "");
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+ @Test
+ public void testSystemPropertyOverrideMultiProtocol() throws Exception {
+ System.setProperty("dubbo.protocol.dubbo.port", "20814");
+ System.setProperty("dubbo.protocol.rmi.port", "10914");
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/override-multi-protocol.xml");
+ providerContext.start();
+ try {
+ ProtocolConfig dubbo = (ProtocolConfig) providerContext.getBean("dubbo");
+ assertEquals(20814, dubbo.getPort().intValue());
+ ProtocolConfig rmi = (ProtocolConfig) providerContext.getBean("rmi");
+ assertEquals(10914, rmi.getPort().intValue());
+ } finally {
+ System.setProperty("dubbo.protocol.dubbo.port", "");
+ System.setProperty("dubbo.protocol.rmi.port", "");
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testSystemPropertyOverrideXmlDefault() throws Exception {
+ System.setProperty("dubbo.application.name", "sysover");
+ System.setProperty("dubbo.application.owner", "sysowner");
+ System.setProperty("dubbo.registry.address", "N/A");
+ System.setProperty("dubbo.protocol.name", "dubbo");
+ System.setProperty("dubbo.protocol.port", "20819");
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/system-properties-override-default.xml");
+ providerContext.start();
+ try {
+ ServiceConfig<DemoService> service = (ServiceConfig<DemoService>) providerContext.getBean("demoServiceConfig");
+ assertEquals("sysover", service.getApplication().getName());
+ assertEquals("sysowner", service.getApplication().getOwner());
+ assertEquals("N/A", service.getRegistry().getAddress());
+ assertEquals("dubbo", service.getProtocol().getName());
+ assertEquals(20819, service.getProtocol().getPort().intValue());
+ } finally {
+ System.setProperty("dubbo.application.name", "");
+ System.setProperty("dubbo.application.owner", "");
+ System.setProperty("dubbo.registry.address", "");
+ System.setProperty("dubbo.protocol.name", "");
+ System.setProperty("dubbo.protocol.port", "");
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testSystemPropertyOverrideXml() throws Exception {
+ System.setProperty("dubbo.application.name", "sysover");
+ System.setProperty("dubbo.application.owner", "sysowner");
+ System.setProperty("dubbo.registry.address", "N/A");
+ System.setProperty("dubbo.protocol.name", "dubbo");
+ System.setProperty("dubbo.protocol.port", "20819");
+ System.setProperty("dubbo.service.register", "false");
+ 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());
+ String register = url.getParameter("register");
+ assertTrue(register != null && !"".equals(register));
+ assertEquals(false, Boolean.valueOf(register));
+ } 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", "");
+ System.setProperty("dubbo.service.register", "");
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+ @Test
+ public void testSystemPropertyOverrideReferenceConfig() throws Exception {
+ System.setProperty("dubbo.reference.retries", "5");
+
+ try {
+ ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
+ service.setInterface(DemoService.class);
+ service.setRef(new DemoServiceImpl());
+ service.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));
+ ProtocolConfig protocolConfig = new ProtocolConfig("injvm");
+ service.setProtocol(protocolConfig);
+ service.export();
+
+ ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
+ reference.setInterface(DemoService.class);
+ reference.setInjvm(true);
+ reference.setRetries(2);
+ reference.get();
+ assertEquals(Integer.valueOf(5), reference.getRetries());
+ } finally {
+ System.setProperty("dubbo.reference.retries", "");
+ }
+ }
+
+ @Test
+ public void testSystemPropertyOverrideApiDefault() throws Exception {
+ System.setProperty("dubbo.application.name", "sysover");
+ System.setProperty("dubbo.application.owner", "sysowner");
+ System.setProperty("dubbo.registry.address", "N/A");
+ System.setProperty("dubbo.protocol.name", "dubbo");
+ System.setProperty("dubbo.protocol.port", "20834");
+ try {
+ ServiceConfig<DemoService> serviceConfig = new ServiceConfig<DemoService>();
+ serviceConfig.setInterface(DemoService.class);
+ serviceConfig.setRef(new DemoServiceImpl());
+ serviceConfig.export();
+ try {
+ assertEquals("sysover", serviceConfig.getApplication().getName());
+ assertEquals("sysowner", serviceConfig.getApplication().getOwner());
+ assertEquals("N/A", serviceConfig.getRegistry().getAddress());
+ assertEquals("dubbo", serviceConfig.getProtocol().getName());
+ assertEquals(20834, serviceConfig.getProtocol().getPort().intValue());
+ } finally {
+ serviceConfig.unexport();
+ }
+ } finally {
+ System.setProperty("dubbo.application.name", "");
+ System.setProperty("dubbo.application.owner", "");
+ System.setProperty("dubbo.registry.address", "");
+ System.setProperty("dubbo.protocol.name", "");
+ System.setProperty("dubbo.protocol.port", "");
+ }
+ }
+
+ @Test
+ public void testSystemPropertyOverrideApi() throws Exception {
+ System.setProperty("dubbo.application.name", "sysover");
+ System.setProperty("dubbo.application.owner", "sysowner");
+ System.setProperty("dubbo.registry.address", "N/A");
+ System.setProperty("dubbo.protocol.name", "dubbo");
+ System.setProperty("dubbo.protocol.port", "20834");
+ try {
+ ApplicationConfig application = new ApplicationConfig();
+ application.setName("aaa");
+
+ RegistryConfig registry = new RegistryConfig();
+ registry.setAddress("127.0.0.1");
+
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setName("rmi");
+ protocol.setPort(1099);
+
+ ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
+ service.setInterface(DemoService.class);
+ service.setRef(new DemoServiceImpl());
+ service.setApplication(application);
+ service.setRegistry(registry);
+ service.setProtocol(protocol);
+ service.export();
+
+ try {
+ URL url = service.toUrls().get(0);
+ assertEquals("sysover", url.getParameter("application"));
+ assertEquals("sysowner", url.getParameter("owner"));
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals(20834, url.getPort());
+ } finally {
+ service.unexport();
+ }
+ } finally {
+ System.setProperty("dubbo.application.name", "");
+ System.setProperty("dubbo.application.owner", "");
+ System.setProperty("dubbo.registry.address", "");
+ System.setProperty("dubbo.protocol.name", "");
+ System.setProperty("dubbo.protocol.port", "");
+ }
+ }
+
+ @Test
+ public void testSystemPropertyOverrideProperties() throws Exception {
+ String portString = System.getProperty( "dubbo.protocol.port");
+ System.clearProperty("dubbo.protocol.port");
+ try {
+ int port = 1234;
+ System.setProperty("dubbo.protocol.port", String.valueOf(port));
+ ApplicationConfig application = new ApplicationConfig();
+ application.setName("aaa");
+
+ RegistryConfig registry = new RegistryConfig();
+ registry.setAddress("N/A");
+
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setName("rmi");
+
+ 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);
+ // from api
+ assertEquals("aaa", url.getParameter("application"));
+ // from dubbo.properties
+ assertEquals("world", url.getParameter("owner"));
+ // from system property
+ assertEquals(1234, url.getPort());
+ } finally {
+ service.unexport();
+ }
+ } finally {
+ if (portString != null) {
+ System.setProperty("dubbo.protocol.port", portString);
+ }
+ }
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testCustomizeParameter() throws Exception {
+ ClassPathXmlApplicationContext context =
+ new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/customize-parameter.xml");
+ context.start();
+ ServiceBean<DemoService> serviceBean = (ServiceBean<DemoService>) context.getBean("demoServiceExport");
+ URL url = (URL) serviceBean.toUrls().get(0);
+ assertEquals("protocol-paramA", url.getParameter("protocol.paramA"));
+ assertEquals("service-paramA", url.getParameter("service.paramA"));
+ }
+
+ @Test
+ public void testPath() throws Exception {
+ ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
+ service.setPath("a/b$c");
+ try {
+ service.setPath("a?b");
+ fail();
+ } catch (IllegalStateException e) {
+ assertTrue(e.getMessage().contains(""));
+ }
+ }
+
+ @Test
+ public void testAnnotation() {
+ SimpleRegistryService registryService = new SimpleRegistryService();
+ Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4548, registryService);
+ try {
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/annotation-provider.xml");
+ providerContext.start();
+ try {
+ ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/annotation-consumer.xml");
+ consumerContext.start();
+ try {
+ AnnotationAction annotationAction = (AnnotationAction) consumerContext.getBean("annotationAction");
+ String hello = annotationAction.doSayName("hello");
+ assertEquals("annotation:hello", hello);
+ } finally {
+ consumerContext.stop();
+ consumerContext.close();
+ }
+ } finally {
+ providerContext.stop();
+ providerContext.close();
+ }
+ } finally {
+ exporter.unexport();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/SimpleRegistryExporter.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/SimpleRegistryExporter.java
new file mode 100644
index 0000000..66f2e09
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/SimpleRegistryExporter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+/**
+ * SimpleRegistryExporter
+ *
+ * @author william.liangf
+ */
+public class SimpleRegistryExporter {
+
+ private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public synchronized static Exporter<RegistryService> exportIfAbsent(int port) {
+ try {
+ new ServerSocket(port).close();
+ return export(port);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public static Exporter<RegistryService> export(int port) {
+ return export(port, new SimpleRegistryService());
+ }
+
+ public static Exporter<RegistryService> export(int port, RegistryService registryService) {
+ return protocol.export(proxyFactory.getInvoker(registryService, RegistryService.class,
+ new URL("dubbo", NetUtils.getLocalHost(), port, RegistryService.class.getName())
+ .setPath(RegistryService.class.getName())
+ .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
+ .addParameter(Constants.CLUSTER_STICKY_KEY, "true")
+ .addParameter(Constants.CALLBACK_INSTANCES_LIMIT_KEY, "1000")
+ .addParameter("ondisconnect", "disconnect")
+ .addParameter("subscribe.1.callback", "true")
+ .addParameter("unsubscribe.1.callback", "false")));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/SimpleRegistryService.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/SimpleRegistryService.java
new file mode 100644
index 0000000..74b6894
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/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.config.spring;
+
+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-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/action/DemoActionByAnnotation.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/action/DemoActionByAnnotation.java
new file mode 100644
index 0000000..e642fed
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/action/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.spring.action;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.alibaba.dubbo.config.spring.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/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/action/DemoActionBySetter.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/action/DemoActionBySetter.java
new file mode 100644
index 0000000..e0c7555
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/action/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.spring.action;
+
+import com.alibaba.dubbo.config.spring.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/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/action/DemoInterceptor.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/action/DemoInterceptor.java
new file mode 100644
index 0000000..2e2857c
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/action/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.spring.action;
+
+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/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/annotation/consumer/AnnotationAction.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/annotation/consumer/AnnotationAction.java
new file mode 100644
index 0000000..e38993b
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/annotation/consumer/AnnotationAction.java
@@ -0,0 +1,38 @@
+/*
+W * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.annotation.consumer;
+
+import org.springframework.stereotype.Controller;
+
+import com.alibaba.dubbo.config.annotation.Reference;
+import com.alibaba.dubbo.config.spring.api.DemoService;
+
+/**
+ * AnnotationAction
+ *
+ * @author william.liangf
+ */
+@Controller("annotationAction")
+public class AnnotationAction {
+
+ @Reference
+ private DemoService demoService;
+
+ public String doSayName(String name) {
+ return demoService.sayName(name);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/annotation/provider/AnnotationServiceImpl.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/annotation/provider/AnnotationServiceImpl.java
new file mode 100644
index 0000000..077b771
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/annotation/provider/AnnotationServiceImpl.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.config.spring.annotation.provider;
+
+import com.alibaba.dubbo.config.annotation.Service;
+import com.alibaba.dubbo.config.spring.api.Box;
+import com.alibaba.dubbo.config.spring.api.DemoService;
+
+/**
+ * DemoServiceImpl
+ *
+ * @author william.liangf
+ */
+@Service
+public class AnnotationServiceImpl implements DemoService {
+
+ public String sayName(String name) {
+ return "annotation:" + name;
+ }
+
+ public Box getBox() {
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/api/Box.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/api/Box.java
new file mode 100644
index 0000000..3e70e34
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/api/Box.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.api;
+
+/**
+ * @author ding.lid
+ */
+public interface Box {
+
+ String getName();
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/api/DemoService.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/api/DemoService.java
new file mode 100644
index 0000000..09d7481
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/api/DemoService.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.config.spring.api;
+
+/**
+ * DemoService
+ *
+ * @author william.liangf
+ */
+public interface DemoService {
+
+ String sayName(String name);
+
+ Box getBox();
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/filter/MockDao.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/filter/MockDao.java
new file mode 100644
index 0000000..8d83565
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/filter/MockDao.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.filter;
+
+/**
+ * MockDao
+ *
+ * @author william.liangf
+ */
+public interface MockDao {
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/filter/MockDaoImpl.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/filter/MockDaoImpl.java
new file mode 100644
index 0000000..7967d65
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/filter/MockDaoImpl.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.filter;
+
+/**
+ * MockDaoImpl
+ *
+ * @author william.liangf
+ */
+public class MockDaoImpl implements MockDao {
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/filter/MockFilter.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/filter/MockFilter.java
new file mode 100644
index 0000000..016590e
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/filter/MockFilter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.filter;
+
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * MockFilter
+ *
+ * @author william.liangf
+ */
+public class MockFilter implements Filter {
+
+ private LoadBalance loadBalance;
+
+ private Protocol protocol;
+
+ private MockDao mockDao;
+
+ public MockDao getMockDao() {
+ return mockDao;
+ }
+
+ public void setMockDao(MockDao mockDao) {
+ this.mockDao = mockDao;
+ }
+
+ public LoadBalance getLoadBalance() {
+ return loadBalance;
+ }
+
+ public void setLoadBalance(LoadBalance loadBalance) {
+ this.loadBalance = loadBalance;
+ }
+
+ public Protocol getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ return invoker.invoke(invocation);
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/DemoServiceImpl.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/DemoServiceImpl.java
new file mode 100644
index 0000000..d0706ad
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/DemoServiceImpl.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.spring.impl;
+
+import com.alibaba.dubbo.config.spring.api.Box;
+import com.alibaba.dubbo.config.spring.api.DemoService;
+
+/**
+ * DemoServiceImpl
+ *
+ * @author william.liangf
+ */
+public class DemoServiceImpl implements DemoService {
+
+ private String prefix = "say:";
+
+ public String sayName(String name) {
+ return prefix + name;
+ }
+
+ public Box getBox() {
+ return null;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/DemoServiceImpl_LongWaiting.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/DemoServiceImpl_LongWaiting.java
new file mode 100644
index 0000000..d3860dc
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/DemoServiceImpl_LongWaiting.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.spring.impl;
+
+import com.alibaba.dubbo.config.spring.api.Box;
+import com.alibaba.dubbo.config.spring.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;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/UnserializableBox.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/UnserializableBox.java
new file mode 100644
index 0000000..389efd1
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/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.spring.impl;
+
+import com.alibaba.dubbo.config.spring.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/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/UnserializableBoxDemoServiceImpl.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/UnserializableBoxDemoServiceImpl.java
new file mode 100644
index 0000000..e0d165d
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/impl/UnserializableBoxDemoServiceImpl.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.spring.impl;
+
+import com.alibaba.dubbo.config.spring.api.Box;
+import com.alibaba.dubbo.config.spring.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();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..d979449
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+mymock=com.alibaba.dubbo.config.spring.filter.MockFilter
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/annotation-consumer.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/annotation-consumer.xml
new file mode 100644
index 0000000..8beac30
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/annotation-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns: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">
+
+ <dubbo:application name="annotation-consumer" />
+ <dubbo:registry address="127.0.0.1:4548" />
+ <dubbo:annotation package="com.alibaba.dubbo.config.spring.annotation.consumer" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/annotation-provider.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/annotation-provider.xml
new file mode 100644
index 0000000..c5f9f80
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/annotation-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">
+
+ <dubbo:application name="annotation-provider" />
+ <dubbo:registry address="127.0.0.1:4548" />
+ <dubbo:annotation package="com.alibaba.dubbo.config.spring.annotation.provider" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/aop-autowire-byname.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/aop-autowire-byname.xml
new file mode 100644
index 0000000..7bcef5b
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/aop-autowire-byname.xml
@@ -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.
+-->
+<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.spring.action.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.spring.api.DemoService" url="dubbo://127.0.0.1:20813" scope="remote">
+ </dubbo:reference>
+
+ <bean id="demoActionBySetter" class="com.alibaba.dubbo.config.spring.action.DemoActionBySetter" />
+
+ <bean id="demoActionByAnnotation" class="com.alibaba.dubbo.config.spring.action.DemoActionByAnnotation" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/aop-autowire-bytype.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/aop-autowire-bytype.xml
new file mode 100644
index 0000000..34dc58a
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.action.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.spring.api.DemoService" url="dubbo://127.0.0.1:20813" />
+
+ <bean id="demoActionBySetter" class="com.alibaba.dubbo.config.spring.action.DemoActionBySetter" />
+
+ <bean id="demoActionByAnnotation" class="com.alibaba.dubbo.config.spring.action.DemoActionByAnnotation" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/customize-parameter.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/customize-parameter.xml
new file mode 100644
index 0000000..672a669
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/customize-parameter.xml
@@ -0,0 +1,35 @@
+<!--
+ - Copyright 1999-2012 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd" default-autowire="byName">
+
+ <dubbo:application name="customize-parameter" />
+
+ <dubbo:registry address="N/A" id="naRegistry" />
+
+ <dubbo:protocol p:protocol-paramA="protocol-paramA" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+ <dubbo:service id="demoServiceExport" p:service-paramA="service-paramA" registry="naRegistry" ref="demoService" interface="com.alibaba.dubbo.config.spring.api.DemoService" />
+
+</beans>
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/delay-fixed-time.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/delay-fixed-time.xml
new file mode 100644
index 0000000..40015d2
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" delay="300" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/delay-on-initialized.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/delay-on-initialized.xml
new file mode 100644
index 0000000..495533c
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" delay="-1" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/demo-provider-UnserializableBox.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/demo-provider-UnserializableBox.xml
new file mode 100644
index 0000000..fbe37a3
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.UnserializableBoxDemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/demo-provider-long-waiting.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/demo-provider-long-waiting.xml
new file mode 100644
index 0000000..8608d89
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" protocol="dubbo1,dubbo2" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl_LongWaiting" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/demo-provider.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/demo-provider.xml
new file mode 100644
index 0000000..85e17d3
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/init-reference-getUrls.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/init-reference-getUrls.xml
new file mode 100644
index 0000000..6aa8b88
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/init-reference-getUrls.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.spring.api.DemoService" url="dubbo://127.0.0.1:20813;dubbo://127.0.0.1:20814" init="true" timeout="100" scope="remote">
+ <dubbo:parameter key="connec.timeout" value="1000"/>
+ </dubbo:reference>
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/init-reference-retry-false.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/init-reference-retry-false.xml
new file mode 100644
index 0000000..17508a8
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" url="dubbo://127.0.0.1:20813;dubbo://127.0.0.1:20814" init="true" timeout="100" scope="remote">
+ <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/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/init-reference.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/init-reference.xml
new file mode 100644
index 0000000..cbcdada
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" url="dubbo://127.0.0.1:20813" init="true" scope="remote" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-protocol-default.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-protocol-default.xml
new file mode 100644
index 0000000..7a2ad9b
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-protocol-default.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" default="false" />
+
+ <dubbo:protocol name="rmi" port="10991" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.spring.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-protocol-error.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-protocol-error.xml
new file mode 100644
index 0000000..bdcd52a
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-protocol-register.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-protocol-register.xml
new file mode 100644
index 0000000..da0bf48
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" protocol="dubbo,rmi" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-protocol.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-protocol.xml
new file mode 100644
index 0000000..0f82f8c
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" protocol="dubbo" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-registry.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-registry.xml
new file mode 100644
index 0000000..6d214ca
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/multi-registry.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 id="reg1" address="127.0.0.1:4545" />
+
+ <dubbo:registry id="reg2" address="127.0.0.1:4546" />
+
+ <dubbo:registry id="reg3" address="127.0.0.1:4547" default="false" />
+
+ <!-- 暴露服务协议配置 -->
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.spring.api.DemoService" ref="demoService" registry="reg2" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/override-multi-protocol.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/override-multi-protocol.xml
new file mode 100644
index 0000000..da06533
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/override-multi-protocol.xml
@@ -0,0 +1,40 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry address="N/A" />
+
+ <!-- 暴露服务协议配置 -->
+ <dubbo:protocol name="dubbo" port="20813" />
+ <dubbo:protocol name="rmi" port="10913" />
+
+ <dubbo:provider protocol="dubbo,rmi" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.spring.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/override-protocol.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/override-protocol.xml
new file mode 100644
index 0000000..1a5bb31
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/provider-nested-service.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/provider-nested-service.xml
new file mode 100644
index 0000000..335c1f9
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/provider-nested-service.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="service-class" />
+
+ <dubbo:registry address="N/A" />
+
+ <dubbo:protocol name="dubbo" port="20887" />
+
+ <dubbo:provider timeout="2000" />
+
+ <dubbo:service id="serviceConfig" interface="com.alibaba.dubbo.config.spring.api.DemoService" ref="demoService" />
+
+ <!-- 嵌套配置 -->
+ <dubbo:provider timeout="1000">
+ <dubbo:service id="serviceConfig2" interface="com.alibaba.dubbo.config.spring.api.DemoService" ref="demoService" group="demo2" />
+ </dubbo:provider>
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/service-class.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/service-class.xml
new file mode 100644
index 0000000..7efed9c
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/service-class.xml
@@ -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.
+-->
+<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="service-class" />
+
+ <dubbo:registry address="N/A" />
+
+ <dubbo:protocol name="dubbo" port="20887" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.spring.api.DemoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl">
+ <property name="prefix" value="welcome:" />
+ </dubbo:service>
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/spring-extension-inject.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/spring-extension-inject.xml
new file mode 100644
index 0000000..84aff91
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/spring-extension-inject.xml
@@ -0,0 +1,36 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry address="N/A" />
+
+ <bean id="mockDao" class="com.alibaba.dubbo.config.spring.filter.MockDaoImpl" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service id="demoServiceConfig" interface="com.alibaba.dubbo.config.spring.api.DemoService" ref="demoService" filter="mymock,default" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/system-properties-override-default.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/system-properties-override-default.xml
new file mode 100644
index 0000000..c7c3672
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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.spring.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/system-properties-override.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/system-properties-override.xml
new file mode 100644
index 0000000..5aee800
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/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" register="true" interface="com.alibaba.dubbo.config.spring.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/xml-override-properties.xml b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/xml-override-properties.xml
new file mode 100644
index 0000000..b70e6f0
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/com/alibaba/dubbo/config/spring/xml-override-properties.xml
@@ -0,0 +1,37 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application id="application" name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry id="registry" address="N/A" />
+
+ <!-- 暴露服务协议配置 -->
+ <dubbo:protocol name="dubbo" port="20813" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service id="demoServiceConfig" interface="com.alibaba.dubbo.config.spring.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.spring.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/dubbo.properties b/dubbo-config/dubbo-config-spring/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..98b869b
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/dubbo.properties
@@ -0,0 +1,5 @@
+dubbo.application.name=hello
+dubbo.application.owner=world
+dubbo.registry.address=10.20.153.17
+dubbo.protocol.port=20881
+dubbo.service.invoke.timeout=2000
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/log4j.xml b/dubbo-config/dubbo-config-spring/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/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-config/pom.xml b/dubbo-config/pom.xml
new file mode 100644
index 0000000..33411c9
--- /dev/null
+++ b/dubbo-config/pom.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.
+-->
+<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.2.2</version>
+ </parent>
+ <artifactId>dubbo-config</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The config module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-config-api</module>
+ <module>dubbo-config-spring</module>
+ </modules>
+</project>
diff --git a/dubbo-container/dubbo-container-api/pom.xml b/dubbo-container/dubbo-container-api/pom.xml
new file mode 100644
index 0000000..2cddce8
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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-container</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-container-api</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Container.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Container.java
new file mode 100644
index 0000000..d65c3c6
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Container.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * Container. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@SPI("spring")
+public interface Container {
+
+ /**
+ * start.
+ */
+ void start();
+
+ /**
+ * stop.
+ */
+ void stop();
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Main.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Main.java
new file mode 100644
index 0000000..3a76713
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Main.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+
+/**
+ * Main. (API, Static, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class Main {
+
+ public static final String CONTAINER_KEY = "dubbo.container";
+
+ private static final Logger logger = LoggerFactory.getLogger(Main.class);
+
+ private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
+
+ private static volatile boolean running = true;
+
+ public static void main(String[] args) {
+ try {
+ if (args == null || args.length == 0) {
+ String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
+ args = Constants.COMMA_SPLIT_PATTERN.split(config);
+ }
+
+ final List<Container> containers = new ArrayList<Container>();
+ for (int i = 0; i < args.length; i ++) {
+ containers.add(loader.getExtension(args[i]));
+ }
+ logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");
+
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void run() {
+ for (Container container : containers) {
+ try {
+ container.stop();
+ logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ synchronized (Main.class) {
+ running = false;
+ Main.class.notify();
+ }
+ }
+ }
+ });
+
+ for (Container container : containers) {
+ container.start();
+ logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
+ }
+ System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ logger.error(e.getMessage(), e);
+ System.exit(1);
+ }
+ synchronized (Main.class) {
+ while (running) {
+ try {
+ Main.class.wait();
+ } catch (Throwable e) {
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/Menu.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/Menu.java
new file mode 100644
index 0000000..4a32298
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/MenuComparator.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/MenuComparator.java
new file mode 100644
index 0000000..7085ed4
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/Page.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/Page.java
new file mode 100644
index 0000000..e843e37
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/Page.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Page
+ *
+ * @author william.liangf
+ */
+public class Page {
+
+ private final String navigation;
+
+ private final String title;
+
+ private final List<String> columns;
+
+ private final List<List<String>> rows;
+
+ public Page(String navigation) {
+ this(navigation, (String) null, (String[]) null, (List<List<String>>) null);
+ }
+
+ public Page(String navigation, String title,
+ String column, String row) {
+ this(navigation, title, column == null ? null : Arrays.asList(new String[]{column}), row == null ? null : stringToList(row));
+ }
+
+ private static List<List<String>> stringToList(String str) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<String> row = new ArrayList<String>();
+ row.add(str);
+ rows.add(row);
+ return rows;
+ }
+
+ public Page(String navigation, String title,
+ String[] columns, List<List<String>> rows) {
+ this(navigation, title, columns == null ? null : Arrays.asList(columns), rows);
+ }
+
+ public Page(String navigation, String title,
+ List<String> columns, List<List<String>> rows) {
+ this.navigation = navigation;
+ this.title = title;
+ this.columns = columns;
+ this.rows = rows;
+ }
+
+ public String getNavigation() {
+ return navigation;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public List<String> getColumns() {
+ return columns;
+ }
+
+ public List<List<String>> getRows() {
+ return rows;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java
new file mode 100644
index 0000000..f15c729
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * PageHandler
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface PageHandler {
+
+ /**
+ * Handle the page.
+ *
+ * @param url
+ * @return the page.
+ */
+ Page handle(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java
new file mode 100644
index 0000000..ad009bb
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+/**
+ * PageServlet
+ *
+ * @author william.liangf
+ */
+public class PageServlet extends HttpServlet {
+
+ private static final long serialVersionUID = -8370312705453328501L;
+
+ protected static final Logger logger = LoggerFactory.getLogger(PageServlet.class);
+
+ protected final Random random = new Random();
+
+ protected final Map<String, PageHandler> pages = new ConcurrentHashMap<String, PageHandler>();
+
+ protected final List<PageHandler> menus = new ArrayList<PageHandler>();
+
+ private static PageServlet INSTANCE;
+
+ public static PageServlet getInstance() {
+ return INSTANCE;
+ }
+
+ public List<PageHandler> getMenus() {
+ return Collections.unmodifiableList(menus);
+ }
+
+ @Override
+ public void init() throws ServletException {
+ super.init();
+ INSTANCE = this;
+ String config = getServletConfig().getInitParameter("pages");
+ Collection<String> names;
+ if (config != null && config.length() > 0) {
+ names = Arrays.asList(Constants.COMMA_SPLIT_PATTERN.split(config));
+ } else {
+ names = ExtensionLoader.getExtensionLoader(PageHandler.class).getSupportedExtensions();
+ }
+ for (String name : names) {
+ PageHandler handler = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtension(name);
+ pages.put(ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler), handler);
+ Menu menu = handler.getClass().getAnnotation(Menu.class);
+ if (menu != null) {
+ menus.add(handler);
+ }
+ }
+ Collections.sort(menus, new MenuComparator());
+ }
+
+ @Override
+ protected final void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ doPost(request, response);
+ }
+
+ @Override
+ protected final void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ if (! response.isCommitted()) {
+ PrintWriter writer = response.getWriter();
+ String uri = request.getRequestURI();
+ boolean isHtml = false;
+ if (uri == null || uri.length() == 0 || "/".equals(uri)) {
+ uri = "index";
+ isHtml = true;
+ } else {
+ if (uri.startsWith("/")) {
+ uri = uri.substring(1);
+ }
+ if (uri.endsWith(".html")) {
+ uri = uri.substring(0, uri.length() - ".html".length());
+ isHtml = true;
+ }
+ }
+ if (uri.endsWith("favicon.ico")) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ ExtensionLoader<PageHandler> pageHandlerLoader = ExtensionLoader.getExtensionLoader(PageHandler.class);
+ PageHandler pageHandler = pageHandlerLoader.hasExtension(uri) ? pageHandlerLoader.getExtension(uri) : null;
+ if (isHtml) {
+ writer.println("<html><head><title>Dubbo</title>");
+ writer.println("<style type=\"text/css\">html, body {margin: 10;padding: 0;background-color: #6D838C;font-family: Arial, Verdana;font-size: 12px;color: #FFFFFF;text-align: center;vertical-align: middle;word-break: break-all; } table {width: 90%; margin: 0px auto;border-collapse: collapse;border: 8px solid #FFFFFF; } thead tr {background-color: #253c46; } tbody tr {background-color: #8da5af; } th {padding-top: 4px;padding-bottom: 4px;font-size: 14px;height: 20px; } td {margin: 3px;padding: 3px;border: 2px solid #FFFFFF;font-size: 14px;height: 25px; } a {color: #FFFFFF;cursor: pointer;text-decoration: underline; } a:hover {text-decoration: none; }</style>");
+ writer.println("</head><body>");
+ }
+ if (pageHandler != null) {
+ Page page = null;
+ try {
+ String query = request.getQueryString();
+ page = pageHandler.handle(URL.valueOf(request.getRequestURL().toString()
+ + (query == null || query.length() == 0 ? "" : "?" + query)));
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ String msg = t.getMessage();
+ if (msg == null) {
+ msg = StringUtils.toString(t);
+ }
+ if (isHtml) {
+ writer.println("<table>");
+ writer.println("<thead>");
+ writer.println(" <tr>");
+ writer.println(" <th>Error</th>");
+ writer.println(" </tr>");
+ writer.println("</thead>");
+ writer.println("<tbody>");
+ writer.println(" <tr>");
+ writer.println(" <td>");
+ writer.println(" " + msg.replace("<", "<").replace(">", "<").replace("\n", "<br/>"));
+ writer.println(" </td>");
+ writer.println(" </tr>");
+ writer.println("</tbody>");
+ writer.println("</table>");
+ writer.println("<br/>");
+ } else {
+ writer.println(msg);
+ }
+ }
+ if (page != null) {
+ if (isHtml) {
+ String nav = page.getNavigation();
+ if (nav == null || nav.length() == 0) {
+ nav = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(pageHandler);
+ nav = nav.substring(0, 1).toUpperCase() + nav.substring(1);
+ }
+ if (! "index".equals(uri)) {
+ nav = "<a href=\"/\">Home</a> > " + nav;
+ }
+ writeMenu(request, writer, nav);
+ writeTable(writer, page.getTitle(), page.getColumns(),
+ page.getRows());
+ } else {
+ if (page.getRows().size() > 0 && page.getRows().get(0).size() > 0) {
+ writer.println(page.getRows().get(0).get(0));
+ }
+ }
+ }
+ } else {
+ if (isHtml) {
+ writer.println("<table>");
+ writer.println("<thead>");
+ writer.println(" <tr>");
+ writer.println(" <th>Error</th>");
+ writer.println(" </tr>");
+ writer.println("</thead>");
+ writer.println("<tbody>");
+ writer.println(" <tr>");
+ writer.println(" <td>");
+ writer.println(" Not found " + uri + " page. Please goto <a href=\"/\">Home</a> page.");
+ writer.println(" </td>");
+ writer.println(" </tr>");
+ writer.println("</tbody>");
+ writer.println("</table>");
+ writer.println("<br/>");
+ } else {
+ writer.println("Not found " + uri + " page.");
+ }
+ }
+ if (isHtml) {
+ writer.println("</body></html>");
+ }
+ writer.flush();
+ }
+ }
+
+ protected final void writeMenu(HttpServletRequest request, PrintWriter writer, String nav) {
+ writer.println("<table>");
+ writer.println("<thead>");
+ writer.println(" <tr>");
+ for (PageHandler handler : menus) {
+ String uri = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler);
+ Menu menu = handler.getClass().getAnnotation(Menu.class);
+ writer.println(" <th><a href=\"" + uri + ".html\">" + menu.name() + "</a></th>");
+ }
+ writer.println(" </tr>");
+ writer.println("</thead>");
+ writer.println("<tbody>");
+ writer.println(" <tr>");
+ writer.println(" <td style=\"text-align: left\" colspan=\"" + menus.size() + "\">");
+ writer.println(nav);
+ writer.println(" </td>");
+ writer.println(" </tr>");
+ writer.println("</tbody>");
+ writer.println("</table>");
+ writer.println("<br/>");
+ }
+
+ protected final void writeTable(PrintWriter writer, String title, List<String> columns,
+ List<List<String>> rows) {
+ int n = random.nextInt();
+ int c = (columns == null ? (rows == null || rows.size() == 0 ? 0 : rows.get(0).size())
+ : columns.size());
+ int r = (rows == null ? 0 : rows.size());
+ writer.println("<table>");
+ writer.println("<thead>");
+ writer.println(" <tr>");
+ writer.println(" <th colspan=\"" + c + "\">" + title + "</th>");
+ writer.println(" </tr>");
+ if (columns != null && columns.size() > 0) {
+ writer.println(" <tr>");
+ for (int i = 0; i < columns.size(); i++) {
+ String col = columns.get(i);
+ if (col.endsWith(":")) {
+ col += " <input type=\"text\" id=\"in_"
+ + n
+ + "_"
+ + i
+ + "\" onkeyup=\"for (var i = 0; i < "
+ + r
+ + "; i ++) { var m = true; for (var j = 0; j < "
+ + columns.size()
+ + "; j ++) { if (document.getElementById('in_"
+ + n
+ + "_' + j)) { var iv = document.getElementById('in_"
+ + n
+ + "_' + j).value; var tv = document.getElementById('td_"
+ + n
+ + "_' + i + '_' + j).innerHTML; if (iv.length > 0 && (tv.length < iv.length || tv.indexOf(iv) == -1)) { m = false; break; } } } document.getElementById('tr_"
+ + n
+ + "_' + i).style.display = (m ? '' : 'none');}\" sytle=\"width: 100%\" />";
+ }
+ writer.println(" <td>" + col + "</td>");
+ }
+ writer.println(" </tr>");
+ }
+ writer.println("</thead>");
+ if (rows != null && rows.size() > 0) {
+ writer.println("<tbody>");
+ int i = 0;
+ for (Collection<String> row : rows) {
+ writer.println(" <tr id=\"tr_" + n + "_" + i + "\">");
+ int j = 0;
+ for (String col : row) {
+ writer.println(" <td id=\"td_" + n + "_" + i + "_" + j
+ + "\" style=\"display: ;\">" + col + "</td>");
+ j++;
+ }
+ writer.println(" </tr>");
+ i++;
+ }
+ writer.println("</tbody>");
+ }
+ writer.println("</table>");
+ writer.println("<br/>");
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/ResourceFilter.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/ResourceFilter.java
new file mode 100644
index 0000000..f72679d
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.java
new file mode 100644
index 0000000..265f980
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.container.page.Menu;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.container.page.PageServlet;
+
+/**
+ * HomePageHandler
+ *
+ * @author william.liangf
+ */
+@Menu(name = "Home", desc = "Home page.", order = Integer.MIN_VALUE)
+public class HomePageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ for (PageHandler handler : PageServlet.getInstance().getMenus()) {
+ String uri = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler);
+ Menu menu = handler.getClass().getAnnotation(Menu.class);
+ List<String> row = new ArrayList<String>();
+ row.add("<a href=\"" + uri + ".html\">" + menu.name() + "</a>");
+ row.add(menu.desc());
+ rows.add(row);
+ }
+ return new Page("Home", "Menus", new String[] {"Menu Name", "Menu Desc"}, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java
new file mode 100644
index 0000000..8891eb2
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page.pages;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Menu;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+
+/**
+ * LogPageHandler
+ *
+ * @author william.liangf
+ */
+@Menu(name = "Log", desc = "Show system log.", order = Integer.MAX_VALUE - 11000)
+public class LogPageHandler implements PageHandler {
+
+ private static final int SHOW_LOG_LENGTH = 30000;
+
+ private File file;
+
+ @SuppressWarnings("unchecked")
+ public LogPageHandler() {
+ try {
+ org.apache.log4j.Logger logger = LogManager.getRootLogger();
+ if (logger != null) {
+ Enumeration<Appender> appenders = logger.getAllAppenders();
+ if (appenders != null) {
+ while (appenders.hasMoreElements()) {
+ Appender appender = appenders.nextElement();
+ if (appender instanceof FileAppender) {
+ FileAppender fileAppender = (FileAppender)appender;
+ String filename = fileAppender.getFile();
+ file = new File(filename);
+ break;
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ }
+ }
+
+ public Page handle(URL url) {
+ long size = 0;
+ String content = "";
+ String modified = "Not exist";
+ if (file != null && file.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ FileChannel channel = fis.getChannel();
+ size = channel.size();
+ ByteBuffer bb;
+ if (size <= SHOW_LOG_LENGTH) {
+ bb = ByteBuffer.allocate((int) size);
+ channel.read(bb, 0);
+ } else {
+ int pos = (int) (size - SHOW_LOG_LENGTH);
+ bb = ByteBuffer.allocate(SHOW_LOG_LENGTH);
+ channel.read(bb, pos);
+ }
+ bb.flip();
+ content = new String(bb.array()).replace("<", "<")
+ .replace(">", ">").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/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.java
new file mode 100644
index 0000000..f22da9b
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page.pages;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.common.status.support.StatusUtils;
+import com.alibaba.dubbo.container.page.Menu;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+
+/**
+ * StatusPageHandler
+ *
+ * @author william.liangf
+ */
+@Menu(name = "Status", desc = "Show system status.", order = Integer.MAX_VALUE - 12000)
+public class StatusPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Set<String> names = ExtensionLoader.getExtensionLoader(StatusChecker.class).getSupportedExtensions();
+ Map<String, Status> statuses = new HashMap<String, Status>();
+ for (String name : names) {
+ StatusChecker checker = ExtensionLoader.getExtensionLoader(StatusChecker.class).getExtension(name);
+ List<String> row = new ArrayList<String>();
+ row.add(name);
+ Status status = checker.check();
+ if (status != null && ! Status.Level.UNKNOWN.equals(status.getLevel())) {
+ statuses.put(name, status);
+ row.add(getLevelHtml(status.getLevel()));
+ row.add(status.getMessage());
+ rows.add(row);
+ }
+ }
+ Status status = StatusUtils.getSummaryStatus(statuses);
+ if ("status".equals(url.getPath())) {
+ return new Page("", "", "", status.getLevel().toString());
+ } else {
+ List<String> row = new ArrayList<String>();
+ row.add("summary");
+ row.add(getLevelHtml(status.getLevel()));
+ row.add("<a href=\"/status\" target=\"_blank\">summary</a>");
+ rows.add(row);
+ return new Page("Status (<a href=\"/status\" target=\"_blank\">summary</a>)", "Status", new String[] {"Name", "Status", "Description"}, rows);
+ }
+ }
+
+ private String getLevelHtml(Status.Level level) {
+ return "<font color=\"" + getLevelColor(level) + "\">" + level.name() + "</font>";
+ }
+
+ private String getLevelColor(Status.Level level) {
+ if (level == Status.Level.OK) {
+ return "green";
+ } else if (level == Status.Level.ERROR) {
+ return "red";
+ } else if (level == Status.Level.WARN) {
+ return "yellow";
+ }
+ return "gray";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java
new file mode 100644
index 0000000..aa8c45a
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page.pages;
+
+import java.lang.management.ManagementFactory;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.page.Menu;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+
+/**
+ * SystemPageHandler
+ *
+ * @author william.liangf
+ */
+@Menu(name = "System", desc = "Show system environment information.", order = Integer.MAX_VALUE - 10000)
+public class SystemPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<String> row;
+
+ row = new ArrayList<String>();
+ row.add("Version");
+ row.add(Version.getVersion(SystemPageHandler.class, "2.0.0"));
+ rows.add(row);
+
+ row = new ArrayList<String>();
+ row.add("Host");
+ String address = NetUtils.getLocalHost();
+ row.add(NetUtils.getHostName(address) + "/" + address);
+ rows.add(row);
+
+ row = new ArrayList<String>();
+ row.add("OS");
+ row.add(System.getProperty("os.name") + " " + System.getProperty("os.version"));
+ rows.add(row);
+
+ row = new ArrayList<String>();
+ row.add("JVM");
+ row.add(System.getProperty("java.runtime.name") + " " + System.getProperty("java.runtime.version") + ",<br/>" + System.getProperty("java.vm.name") + " " + System.getProperty("java.vm.version") + " " + System.getProperty("java.vm.info", ""));
+ rows.add(row);
+
+ row = new ArrayList<String>();
+ row.add("CPU");
+ row.add(System.getProperty("os.arch", "") + ", " + String.valueOf(Runtime.getRuntime().availableProcessors()) + " cores");
+ rows.add(row);
+
+ row = new ArrayList<String>();
+ row.add("Locale");
+ row.add(Locale.getDefault().toString() + "/" + System.getProperty("file.encoding"));
+ rows.add(row);
+
+ row = new ArrayList<String>();
+ row.add("Uptime");
+ row.add(formatUptime(ManagementFactory.getRuntimeMXBean().getUptime()));
+ rows.add(row);
+
+ row = new ArrayList<String>();
+ row.add("Time");
+ row.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z").format(new Date()));
+ rows.add(row);
+
+ return new Page("System", "System", new String[] {
+ "Property", "Value" }, rows);
+ }
+
+ private static final long SECOND = 1000;
+
+ private static final long MINUTE = 60 * SECOND;
+
+ private static final long HOUR = 60 * MINUTE;
+
+ private static final long DAY = 24 * HOUR;
+
+ private String formatUptime(long uptime) {
+ StringBuilder buf = new StringBuilder();
+ if (uptime > DAY) {
+ long days = (uptime - uptime % DAY) / DAY;
+ buf.append(days);
+ buf.append(" Days");
+ uptime = uptime % DAY;
+ }
+ if (uptime > HOUR) {
+ long hours = (uptime - uptime % HOUR) / HOUR;
+ if (buf.length() > 0) {
+ buf.append(", ");
+ }
+ buf.append(hours);
+ buf.append(" Hours");
+ uptime = uptime % HOUR;
+ }
+ if (uptime > MINUTE) {
+ long minutes = (uptime - uptime % MINUTE) / MINUTE;
+ if (buf.length() > 0) {
+ buf.append(", ");
+ }
+ buf.append(minutes);
+ buf.append(" Minutes");
+ uptime = uptime % MINUTE;
+ }
+ if (uptime > SECOND) {
+ long seconds = (uptime - uptime % SECOND) / SECOND;
+ if (buf.length() > 0) {
+ buf.append(", ");
+ }
+ buf.append(seconds);
+ buf.append(" Seconds");
+ uptime = uptime % SECOND;
+ }
+ if (uptime > 0) {
+ if (buf.length() > 0) {
+ buf.append(", ");
+ }
+ buf.append(uptime);
+ buf.append(" Milliseconds");
+ }
+ return buf.toString();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/dump.sh b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/dump.sh
new file mode 100644
index 0000000..2458c43
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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/dubbo-container-api/src/main/resources/META-INF/assembly/bin/restart.sh b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/restart.sh
new file mode 100644
index 0000000..647ec19
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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/dubbo-container-api/src/main/resources/META-INF/assembly/bin/server.sh b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/server.sh
new file mode 100644
index 0000000..90947a5
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.bat b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.bat
new file mode 100644
index 0000000..f91d023
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh
new file mode 100644
index 0000000..e8b2756
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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/dubbo-container-api/src/main/resources/META-INF/assembly/bin/stop.sh b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/stop.sh
new file mode 100644
index 0000000..506ee0a
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/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/dubbo-container-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..c825208
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
@@ -0,0 +1,3 @@
+spring=com.alibaba.dubbo.container.spring.SpringContainer
+jetty=com.alibaba.dubbo.container.jetty.JettyContainer
+log4j=com.alibaba.dubbo.container.log4j.Log4jContainer
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..e1c39d6
--- /dev/null
+++ b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,4 @@
+index=com.alibaba.dubbo.container.page.pages.HomePageHandler
+status=com.alibaba.dubbo.container.page.pages.StatusPageHandler
+log=com.alibaba.dubbo.container.page.pages.LogPageHandler
+system=com.alibaba.dubbo.container.page.pages.SystemPageHandler
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-jetty/pom.xml b/dubbo-container/dubbo-container-jetty/pom.xml
new file mode 100644
index 0000000..55f6b26
--- /dev/null
+++ b/dubbo-container/dubbo-container-jetty/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-container</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-container-jetty</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The jetty container module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-container-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-jetty/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.java b/dubbo-container/dubbo-container-jetty/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.java
new file mode 100644
index 0000000..e9fae86
--- /dev/null
+++ b/dubbo-container/dubbo-container-jetty/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.jetty;
+
+import org.mortbay.jetty.Handler;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.servlet.FilterHolder;
+import org.mortbay.jetty.servlet.ServletHandler;
+import org.mortbay.jetty.servlet.ServletHolder;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.Container;
+import com.alibaba.dubbo.container.page.PageServlet;
+import com.alibaba.dubbo.container.page.ResourceFilter;
+
+/**
+ * JettyContainer. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class JettyContainer implements Container {
+
+ private static final Logger logger = LoggerFactory.getLogger(JettyContainer.class);
+
+ public static final String JETTY_PORT = "dubbo.jetty.port";
+
+ public static final String JETTY_DIRECTORY = "dubbo.jetty.directory";
+
+ public static final String JETTY_PAGES = "dubbo.jetty.page";
+
+ public static final int DEFAULT_JETTY_PORT = 8080;
+
+ SelectChannelConnector connector;
+
+ public void start() {
+ String serverPort = ConfigUtils.getProperty(JETTY_PORT);
+ int port;
+ if (serverPort == null || serverPort.length() == 0) {
+ port = DEFAULT_JETTY_PORT;
+ } else {
+ port = Integer.parseInt(serverPort);
+ }
+ connector = new SelectChannelConnector();
+ connector.setPort(port);
+ ServletHandler handler = new ServletHandler();
+
+ String resources = ConfigUtils.getProperty(JETTY_DIRECTORY);
+ if (resources != null && resources.length() > 0) {
+ FilterHolder resourceHolder = handler.addFilterWithMapping(ResourceFilter.class, "/*", Handler.DEFAULT);
+ resourceHolder.setInitParameter("resources", resources);
+ }
+
+ ServletHolder pageHolder = handler.addServletWithMapping(PageServlet.class, "/*");
+ pageHolder.setInitParameter("pages", ConfigUtils.getProperty(JETTY_PAGES));
+ pageHolder.setInitOrder(2);
+
+ Server server = new Server();
+ server.addConnector(connector);
+ server.addHandler(handler);
+ try {
+ server.start();
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to start jetty server on " + NetUtils.getLocalHost() + ":" + port + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ public void stop() {
+ try {
+ if (connector != null) {
+ connector.close();
+ connector = null;
+ }
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-jetty/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container b/dubbo-container/dubbo-container-jetty/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..880fcc8
--- /dev/null
+++ b/dubbo-container/dubbo-container-jetty/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
@@ -0,0 +1 @@
+jetty=com.alibaba.dubbo.container.jetty.JettyContainer
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-jetty/src/test/java/com/alibaba/dubbo/container/jetty/JettyContainerTest.java b/dubbo-container/dubbo-container-jetty/src/test/java/com/alibaba/dubbo/container/jetty/JettyContainerTest.java
new file mode 100644
index 0000000..9b2c009
--- /dev/null
+++ b/dubbo-container/dubbo-container-jetty/src/test/java/com/alibaba/dubbo/container/jetty/JettyContainerTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.jetty;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.container.Container;
+
+/**
+ * StandaloneContainerTest
+ *
+ * @author william.liangf
+ */
+public class JettyContainerTest {
+
+ @Test
+ public void testContainer() {
+ JettyContainer container = (JettyContainer) ExtensionLoader.getExtensionLoader(Container.class).getExtension("jetty");
+ container.start();
+ Assert.assertTrue(container.connector.isStarted());
+ container.stop();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-log4j/pom.xml b/dubbo-container/dubbo-container-log4j/pom.xml
new file mode 100644
index 0000000..43b02fb
--- /dev/null
+++ b/dubbo-container/dubbo-container-log4j/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-container</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-container-log4j</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The log4j container module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-container-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-log4j/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.java b/dubbo-container/dubbo-container-log4j/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.java
new file mode 100644
index 0000000..68e1335
--- /dev/null
+++ b/dubbo-container/dubbo-container-log4j/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.log4j;
+
+import java.util.Enumeration;
+import java.util.Properties;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PropertyConfigurator;
+
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.container.Container;
+
+/**
+ * Log4jContainer. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class Log4jContainer implements Container {
+
+ public static final String LOG4J_FILE = "dubbo.log4j.file";
+
+ public static final String LOG4J_LEVEL = "dubbo.log4j.level";
+
+ public static final String LOG4J_SUBDIRECTORY = "dubbo.log4j.subdirectory";
+
+ public static final String DEFAULT_LOG4J_LEVEL = "ERROR";
+
+ @SuppressWarnings("unchecked")
+ public void start() {
+ String file = ConfigUtils.getProperty(LOG4J_FILE);
+ if (file != null && file.length() > 0) {
+ String level = ConfigUtils.getProperty(LOG4J_LEVEL);
+ if (level == null || level.length() == 0) {
+ level = DEFAULT_LOG4J_LEVEL;
+ }
+ Properties properties = new Properties();
+ properties.setProperty("log4j.rootLogger", level + ",application");
+ properties.setProperty("log4j.appender.application", "org.apache.log4j.DailyRollingFileAppender");
+ properties.setProperty("log4j.appender.application.File", file);
+ properties.setProperty("log4j.appender.application.Append", "true");
+ properties.setProperty("log4j.appender.application.DatePattern", "'.'yyyy-MM-dd");
+ properties.setProperty("log4j.appender.application.layout", "org.apache.log4j.PatternLayout");
+ properties.setProperty("log4j.appender.application.layout.ConversionPattern", "%d [%t] %-5p %C{6} (%F:%L) - %m%n");
+ PropertyConfigurator.configure(properties);
+ }
+ String subdirectory = ConfigUtils.getProperty(LOG4J_SUBDIRECTORY);
+ if (subdirectory != null && subdirectory.length() > 0) {
+ Enumeration<org.apache.log4j.Logger> ls = LogManager.getCurrentLoggers();
+ while (ls.hasMoreElements()) {
+ org.apache.log4j.Logger l = ls.nextElement();
+ if (l != null) {
+ Enumeration<Appender> as = l.getAllAppenders();
+ while (as.hasMoreElements()) {
+ Appender a = as.nextElement();
+ if (a instanceof FileAppender) {
+ FileAppender fa = (FileAppender)a;
+ String f = fa.getFile();
+ if (f != null && f.length() > 0) {
+ int i = f.replace('\\', '/').lastIndexOf('/');
+ String path;
+ if (i == -1) {
+ path = subdirectory;
+ } else {
+ path = f.substring(0, i);
+ if (! path.endsWith(subdirectory)) {
+ path = path + "/" + subdirectory;
+ }
+ f = f.substring(i + 1);
+ }
+ fa.setFile(path + "/" + f);
+ fa.activateOptions();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void stop() {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-log4j/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container b/dubbo-container/dubbo-container-log4j/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..2b2fc2d
--- /dev/null
+++ b/dubbo-container/dubbo-container-log4j/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
@@ -0,0 +1 @@
+log4j=com.alibaba.dubbo.container.log4j.Log4jContainer
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-log4j/src/test/java/com/alibaba/dubbo/container/log4j/Log4jContainerTest.java b/dubbo-container/dubbo-container-log4j/src/test/java/com/alibaba/dubbo/container/log4j/Log4jContainerTest.java
new file mode 100644
index 0000000..6d68eb4
--- /dev/null
+++ b/dubbo-container/dubbo-container-log4j/src/test/java/com/alibaba/dubbo/container/log4j/Log4jContainerTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.log4j;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.container.Container;
+
+/**
+ * StandaloneContainerTest
+ *
+ * @author william.liangf
+ */
+public class Log4jContainerTest {
+
+ @Test
+ public void testContainer() {
+ Log4jContainer container = (Log4jContainer) ExtensionLoader.getExtensionLoader(Container.class).getExtension("log4j");
+ container.start();
+ container.stop();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-spring/pom.xml b/dubbo-container/dubbo-container-spring/pom.xml
new file mode 100644
index 0000000..79b80c4
--- /dev/null
+++ b/dubbo-container/dubbo-container-spring/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-container</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-container-spring</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The spring container module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-container-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java b/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java
new file mode 100644
index 0000000..c955259
--- /dev/null
+++ b/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.spring;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.container.Container;
+
+/**
+ * SpringContainer. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class SpringContainer implements Container {
+
+ private static final Logger logger = LoggerFactory.getLogger(SpringContainer.class);
+
+ public static final String SPRING_CONFIG = "dubbo.spring.config";
+
+ public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml";
+
+ ClassPathXmlApplicationContext context;
+
+ public void start() {
+ String configPath = ConfigUtils.getProperty(SPRING_CONFIG);
+ if (configPath == null || configPath.length() == 0) {
+ configPath = DEFAULT_SPRING_CONFIG;
+ }
+ context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"));
+ context.start();
+ }
+
+ public void stop() {
+ try {
+ if (context != null) {
+ context.stop();
+ context.close();
+ context = null;
+ }
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-spring/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container b/dubbo-container/dubbo-container-spring/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..95141b1
--- /dev/null
+++ b/dubbo-container/dubbo-container-spring/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
@@ -0,0 +1 @@
+spring=com.alibaba.dubbo.container.spring.SpringContainer
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-spring/src/test/java/com/alibaba/dubbo/container/spring/SpringContainerTest.java b/dubbo-container/dubbo-container-spring/src/test/java/com/alibaba/dubbo/container/spring/SpringContainerTest.java
new file mode 100644
index 0000000..678d3a1
--- /dev/null
+++ b/dubbo-container/dubbo-container-spring/src/test/java/com/alibaba/dubbo/container/spring/SpringContainerTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.spring;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.container.Container;
+
+/**
+ * StandaloneContainerTest
+ *
+ * @author william.liangf
+ */
+public class SpringContainerTest {
+
+ @Test
+ public void testContainer() {
+ SpringContainer container = (SpringContainer) ExtensionLoader.getExtensionLoader(Container.class).getExtension("spring");
+ container.start();
+ Assert.assertEquals(SpringContainer.class, container.context.getBean("container").getClass());
+ container.stop();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-spring/src/test/resources/META-INF/spring/test.xml b/dubbo-container/dubbo-container-spring/src/test/resources/META-INF/spring/test.xml
new file mode 100644
index 0000000..4ddff13
--- /dev/null
+++ b/dubbo-container/dubbo-container-spring/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-container/dubbo-container-spring/src/test/resources/log4j.xml b/dubbo-container/dubbo-container-spring/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-container/dubbo-container-spring/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..a76b006
--- /dev/null
+++ b/dubbo-container/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.2.2</version>
+ </parent>
+ <artifactId>dubbo-container</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The container module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-container-api</module>
+ <module>dubbo-container-spring</module>
+ <module>dubbo-container-jetty</module>
+ <module>dubbo-container-log4j</module>
+ </modules>
+</project>
diff --git a/dubbo-demo/dubbo-demo-api/pom.xml b/dubbo-demo/dubbo-demo-api/pom.xml
new file mode 100644
index 0000000..1dc2670
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-api/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-demo</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-demo-api</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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/dubbo-demo-api/src/main/java/com/alibaba/dubbo/demo/DemoService.java b/dubbo-demo/dubbo-demo-api/src/main/java/com/alibaba/dubbo/demo/DemoService.java
new file mode 100644
index 0000000..7dda95f
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-api/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-demo/dubbo-demo-consumer/pom.xml b/dubbo-demo/dubbo-demo-consumer/pom.xml
new file mode 100644
index 0000000..1b2757f
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-consumer/pom.xml
@@ -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.
+-->
+<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-demo</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-demo-consumer</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The demo consumer module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-demo-api</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>redis.clients</groupId>
+ <artifactId>jedis</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>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.cache</groupId>
+ <artifactId>cache-api</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/dubbo-demo-consumer/src/main/assembly/assembly.xml b/dubbo-demo/dubbo-demo-consumer/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..25235d9
--- /dev/null
+++ b/dubbo-demo/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/dubbo-demo-consumer/src/main/assembly/conf/dubbo.properties b/dubbo-demo/dubbo-demo-consumer/src/main/assembly/conf/dubbo.properties
new file mode 100644
index 0000000..d3cc21f
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-consumer/src/main/assembly/conf/dubbo.properties
@@ -0,0 +1,25 @@
+##
+# Copyright 1999-2011 Alibaba Group.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+dubbo.container=log4j,spring
+dubbo.application.name=demo-consumer
+dubbo.application.owner=
+dubbo.registry.address=multicast://224.5.6.7:1234
+#dubbo.registry.address=zookeeper://127.0.0.1:2181
+#dubbo.registry.address=redis://127.0.0.1:6379
+#dubbo.registry.address=dubbo://127.0.0.1:9090
+dubbo.monitor.protocol=registry
+dubbo.log4j.file=logs/dubbo-demo-consumer.log
+dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-consumer/src/main/java/com/alibaba/dubbo/demo/consumer/DemoAction.java b/dubbo-demo/dubbo-demo-consumer/src/main/java/com/alibaba/dubbo/demo/consumer/DemoAction.java
new file mode 100644
index 0000000..5258feb
--- /dev/null
+++ b/dubbo-demo/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() throws Exception {
+ 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);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ Thread.sleep(2000);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-consumer/src/main/resources/META-INF/spring/dubbo-demo-action.xml b/dubbo-demo/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/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/dubbo-demo-consumer/src/main/resources/META-INF/spring/dubbo-demo-consumer.xml b/dubbo-demo/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/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/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/AdminConsumer.java b/dubbo-demo/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/AdminConsumer.java
new file mode 100644
index 0000000..0a41ee5
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/AdminConsumer.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.demo.consumer;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.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.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * RegistryContainer
+ *
+ * @author william.liangf
+ */
+public class AdminConsumer {
+
+ private static final Logger logger = LoggerFactory.getLogger(AdminConsumer.class);
+
+ public static final String REGISTRY_ADDRESS = "dubbo.registry.address";
+
+ private final RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
+
+ private Registry registry;
+
+ public Registry getRegistry() {
+ return registry;
+ }
+
+ public static void main(String[] args) throws Exception {
+ AdminConsumer c = new AdminConsumer();
+ c.start();
+ System.in.read();
+ c.stop();
+ }
+
+ 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).addParameter(Constants.CHECK_KEY, String.valueOf(false));
+ registry = registryFactory.getRegistry(registryUrl);
+ URL subscribeUrl = new URL(Constants.ADMIN_PROTOCOL, NetUtils.getLocalHost(), 0, "",
+ Constants.INTERFACE_KEY, Constants.ANY_VALUE,
+ Constants.GROUP_KEY, Constants.ANY_VALUE,
+ Constants.VERSION_KEY, Constants.ANY_VALUE,
+ Constants.CLASSIFIER_KEY, Constants.ANY_VALUE,
+ Constants.CATEGORY_KEY, Constants.ANY_VALUE,
+ Constants.ENABLED_KEY, Constants.ANY_VALUE,
+ Constants.CHECK_KEY, String.valueOf(false));
+ registry.subscribe(subscribeUrl, new NotifyListener() {
+ public void notify(List<URL> urls) {
+ if (urls == null || urls.size() == 0) {
+ return;
+ }
+ System.out.println("-----------(size:" + urls.size() + ") " + urls);
+ }
+ });
+ }
+
+ public void stop() {
+ try {
+ registry.destroy();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/DemoConsumer.java b/dubbo-demo/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/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/dubbo-demo-consumer/src/test/java/com/alibaba/dubbo/demo/consumer/ExitConsumer.java b/dubbo-demo/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/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/dubbo-demo-consumer/src/test/resources/dubbo.properties b/dubbo-demo/dubbo-demo-consumer/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..5d04fe8
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-consumer/src/test/resources/dubbo.properties
@@ -0,0 +1,25 @@
+##
+# Copyright 1999-2011 Alibaba Group.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+dubbo.container=log4j,spring
+dubbo.application.name=demo-consumer
+dubbo.application.owner=
+dubbo.registry.address=multicast://224.5.6.7:1234
+#dubbo.registry.address=zookeeper://127.0.0.1:2181
+#dubbo.registry.address=redis://127.0.0.1:6379
+#dubbo.registry.address=dubbo://127.0.0.1:9090
+dubbo.monitor.protocol=registry
+#dubbo.log4j.file=logs/dubbo-demo-consumer.log
+#dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-consumer/src/test/resources/log4j.xml b/dubbo-demo/dubbo-demo-consumer/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-demo/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/dubbo-demo-examples/pom.xml b/dubbo-demo/dubbo-demo-examples/pom.xml
new file mode 100644
index 0000000..206e718
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/pom.xml
@@ -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.
+-->
+<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-demo</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-demo-examples</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The examples demo module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-config-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-config-spring</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-mina</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-grizzly</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-p2p</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-http</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-default</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-injvm</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-rmi</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-hessian</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-default</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-multicast</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-zookeeper</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-monitor-default</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${basedir}/src/main/java</directory>
+ <excludes>
+ <exclude>**/*.java</exclude>
+ </excludes>
+ </resource>
+ <resource>
+ <directory>${basedir}/src/main/resources</directory>
+ </resource>
+ </resources>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/AnnotationConsumer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/AnnotationConsumer.java
new file mode 100644
index 0000000..eea49d9
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/AnnotationConsumer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.annotation;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.annotation.action.AnnotationAction;
+
+/**
+ * CallbackConsumer
+ *
+ * @author william.liangf
+ */
+public class AnnotationConsumer {
+
+ public static void main(String[] args) throws Exception {
+ String config = AnnotationConsumer.class.getPackage().getName().replace('.', '/') + "/annotation-consumer.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ final AnnotationAction annotationAction = (AnnotationAction)context.getBean("annotationAction");
+ String hello = annotationAction.doSayHello("world");
+ System.out.println("result :" + hello);
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/AnnotationProvider.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/AnnotationProvider.java
new file mode 100644
index 0000000..6185a99
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/AnnotationProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.annotation;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * MergeProvider
+ *
+ * @author william.liangf
+ */
+public class AnnotationProvider {
+
+ public static void main(String[] args) throws Exception {
+ String config = AnnotationProvider.class.getPackage().getName().replace('.', '/') + "/annotation-provider.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/action/AnnotationAction.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/action/AnnotationAction.java
new file mode 100644
index 0000000..0d82b44
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/action/AnnotationAction.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.annotation.action;
+
+import org.springframework.stereotype.Component;
+
+import com.alibaba.dubbo.config.annotation.Reference;
+import com.alibaba.dubbo.examples.annotation.api.AnnotationService;
+
+/**
+ * AnnotationAction
+ *
+ * @author william.liangf
+ */
+@Component("annotationAction")
+public class AnnotationAction {
+
+ @Reference
+ private AnnotationService annotationService;
+
+ public String doSayHello(String name) {
+ return annotationService.sayHello(name);
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/annotation-consumer.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/annotation-consumer.xml
new file mode 100644
index 0000000..dbf8a3e
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/annotation-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="annotation-consumer" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:annotation package="com.alibaba.dubbo.examples.annotation.action" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/annotation-provider.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/annotation-provider.xml
new file mode 100644
index 0000000..4e38e93
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/annotation-provider.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="annotation-provider" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <dubbo:annotation package="com.alibaba.dubbo.examples.annotation.impl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/api/AnnotationService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/api/AnnotationService.java
new file mode 100644
index 0000000..6d35a8c
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/api/AnnotationService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.annotation.api;
+
+/**
+ * AsyncService
+ *
+ * @author william.liangf
+ */
+public interface AnnotationService {
+
+ String sayHello(String name);
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/impl/AnnotationServiceImpl.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/impl/AnnotationServiceImpl.java
new file mode 100644
index 0000000..d0d29c1
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/annotation/impl/AnnotationServiceImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.annotation.impl;
+
+import com.alibaba.dubbo.config.annotation.Service;
+import com.alibaba.dubbo.examples.annotation.api.AnnotationService;
+
+/**
+ * AsyncServiceImpl
+ *
+ * @author william.liangf
+ */
+@Service
+public class AnnotationServiceImpl implements AnnotationService {
+
+ public String sayHello(String name) {
+ System.out.println("async provider received: " + name);
+ return "annotation: hello, " + name;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/AsyncConsumer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/AsyncConsumer.java
new file mode 100644
index 0000000..3bb73e0
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/AsyncConsumer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.async;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.async.api.AsyncService;
+import com.alibaba.dubbo.rpc.RpcContext;
+
+/**
+ * CallbackConsumer
+ *
+ * @author william.liangf
+ */
+public class AsyncConsumer {
+
+ public static void main(String[] args) throws Exception {
+ String config = AsyncConsumer.class.getPackage().getName().replace('.', '/') + "/async-consumer.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+
+ final AsyncService asyncService = (AsyncService)context.getBean("asyncService");
+
+ Future<String> f = RpcContext.getContext().asyncCall(new Callable<String>() {
+ public String call() throws Exception {
+ return asyncService.sayHello("async call request");
+ }
+ });
+
+ System.out.println("async call ret :" + f.get());
+
+ RpcContext.getContext().asyncCall(new Runnable() {
+ public void run() {
+ asyncService.sayHello("oneway call request1");
+ asyncService.sayHello("oneway call request2");
+ }
+ });
+
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/AsyncProvider.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/AsyncProvider.java
new file mode 100644
index 0000000..15993fb
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/AsyncProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.async;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * MergeProvider
+ *
+ * @author william.liangf
+ */
+public class AsyncProvider {
+
+ public static void main(String[] args) throws Exception {
+ String config = AsyncProvider.class.getPackage().getName().replace('.', '/') + "/async-provider.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/api/AsyncService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/api/AsyncService.java
new file mode 100644
index 0000000..b659e38
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/api/AsyncService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.async.api;
+
+/**
+ * AsyncService
+ *
+ * @author william.liangf
+ */
+public interface AsyncService {
+
+ String sayHello(String name);
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/async-consumer.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/async-consumer.xml
new file mode 100644
index 0000000..71a54a2
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/async-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="async-consumer" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:reference id="asyncService" interface="com.alibaba.dubbo.examples.async.api.AsyncService" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/async-provider.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/async-provider.xml
new file mode 100644
index 0000000..a40a3e8
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/async-provider.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="async-provider" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <bean id="asyncService" class="com.alibaba.dubbo.examples.async.impl.AsyncServiceImpl" />
+
+ <dubbo:service interface="com.alibaba.dubbo.examples.async.api.AsyncService" ref="asyncService" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/impl/AsyncServiceImpl.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/impl/AsyncServiceImpl.java
new file mode 100644
index 0000000..52cf92b
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/async/impl/AsyncServiceImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.async.impl;
+
+import com.alibaba.dubbo.examples.async.api.AsyncService;
+
+/**
+ * AsyncServiceImpl
+ *
+ * @author william.liangf
+ */
+public class AsyncServiceImpl implements AsyncService {
+
+ public String sayHello(String name) {
+ System.out.println("async provider received: " + name);
+ return "hello, " + name;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheConsumer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheConsumer.java
new file mode 100644
index 0000000..ec70d79
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheConsumer.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.cache;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.cache.api.CacheService;
+
+/**
+ * CacheConsumer
+ *
+ * @author william.liangf
+ */
+public class CacheConsumer {
+
+ public static void main(String[] args) throws Exception {
+ String config = CacheConsumer.class.getPackage().getName().replace('.', '/') + "/cache-consumer.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+
+ CacheService cacheService = (CacheService)context.getBean("cacheService");
+
+ // 测试缓存生效,多次调用返回同样的结果。(服务器端自增长返回值)
+ String fix = null;
+ for (int i = 0; i < 5; i ++) {
+ String result = cacheService.findCache("0");
+ if (fix == null || fix.equals(result)) {
+ System.out.println("OK: " + result);
+ } else {
+ System.err.println("ERROR: " + result);
+ }
+ fix = result;
+ Thread.sleep(500);
+ }
+
+ // LRU的缺省cache.size为1000,执行1001次,应有溢出
+ for (int n = 0; n < 1001; n ++) {
+ String pre = null;
+ for (int i = 0; i < 10; i ++) {
+ String result = cacheService.findCache(String.valueOf(n));
+ if (pre != null && ! pre.equals(result)) {
+ System.err.println("ERROR: " + result);
+ }
+ pre = result;
+ }
+ }
+
+ // 测试LRU有移除最开始的一个缓存项
+ String result = cacheService.findCache("0");
+ if (fix != null && ! fix.equals(result)) {
+ System.out.println("OK: " + result);
+ } else {
+ System.err.println("ERROR: " + result);
+ }
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheProvider.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheProvider.java
new file mode 100644
index 0000000..88dd11b
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/CacheProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.cache;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * CacheProvider
+ *
+ * @author william.liangf
+ */
+public class CacheProvider {
+
+ public static void main(String[] args) throws Exception {
+ String config = CacheProvider.class.getPackage().getName().replace('.', '/') + "/cache-provider.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/api/CacheService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/api/CacheService.java
new file mode 100644
index 0000000..90facfd
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/api/CacheService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.cache.api;
+
+/**
+ * ValidationService
+ *
+ * @author william.liangf
+ */
+public interface CacheService {
+
+ String findCache(String id);
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-consumer.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-consumer.xml
new file mode 100644
index 0000000..32dfb10
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="cache-consumer" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:reference id="cacheService" interface="com.alibaba.dubbo.examples.cache.api.CacheService" cache="true" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-provider.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-provider.xml
new file mode 100644
index 0000000..1875559
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/cache-provider.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="cache-provider" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <bean id="cacheService" class="com.alibaba.dubbo.examples.cache.impl.CacheServiceImpl" />
+
+ <dubbo:service interface="com.alibaba.dubbo.examples.cache.api.CacheService" ref="cacheService" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/impl/CacheServiceImpl.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/impl/CacheServiceImpl.java
new file mode 100644
index 0000000..5710e0f
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/cache/impl/CacheServiceImpl.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.cache.impl;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.examples.cache.api.CacheService;
+
+/**
+ * ValidationServiceImpl
+ *
+ * @author william.liangf
+ */
+public class CacheServiceImpl implements CacheService {
+
+ private final AtomicInteger i = new AtomicInteger();
+
+ public String findCache(String id) {
+ return "request: " + id + ", response: " + i.getAndIncrement();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackConsumer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackConsumer.java
new file mode 100644
index 0000000..576762b
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackConsumer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.callback;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.callback.api.CallbackListener;
+import com.alibaba.dubbo.examples.callback.api.CallbackService;
+
+/**
+ * CallbackConsumer
+ *
+ * @author william.liangf
+ */
+public class CallbackConsumer {
+
+ public static void main(String[] args) throws Exception {
+ String config = CallbackConsumer.class.getPackage().getName().replace('.', '/') + "/callback-consumer.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ CallbackService callbackService = (CallbackService) context.getBean("callbackService");
+ callbackService.addListener("foo.bar", new CallbackListener() {
+ public void changed(String msg) {
+ System.out.println("callback1:" + msg);
+ }
+ });
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackProvider.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackProvider.java
new file mode 100644
index 0000000..a530d97
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/CallbackProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.callback;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * MergeProvider
+ *
+ * @author william.liangf
+ */
+public class CallbackProvider {
+
+ public static void main(String[] args) throws Exception {
+ String config = CallbackProvider.class.getPackage().getName().replace('.', '/') + "/callback-provider.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackListener.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackListener.java
new file mode 100644
index 0000000..bc7d172
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.callback.api;
+
+/**
+ * CallbackListener
+ *
+ * @author william.liangf
+ */
+public interface CallbackListener {
+
+ void changed(String msg);
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackService.java
new file mode 100644
index 0000000..93675aa
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/api/CallbackService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.callback.api;
+
+/**
+ * CallbackService
+ *
+ * @author william.liangf
+ */
+public interface CallbackService {
+
+ void addListener(String key, CallbackListener listener);
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-consumer.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-consumer.xml
new file mode 100644
index 0000000..66cdaca
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="callback-consumer" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:reference id="callbackService" interface="com.alibaba.dubbo.examples.callback.api.CallbackService" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-provider.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-provider.xml
new file mode 100644
index 0000000..013abe1
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/callback-provider.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="callback-provider" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <bean id="callbackService" class="com.alibaba.dubbo.examples.callback.impl.CallbackServiceImpl" />
+
+ <dubbo:service interface="com.alibaba.dubbo.examples.callback.api.CallbackService" ref="callbackService" connections="1" callbacks="1000">
+ <dubbo:method name="addListener">
+ <dubbo:argument index="1" callback="true" />
+ <!--<dubbo:argument type="com.demo.CallbackListener" callback="true" />-->
+ </dubbo:method>
+ </dubbo:service>
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/impl/CallbackServiceImpl.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/impl/CallbackServiceImpl.java
new file mode 100644
index 0000000..c904b33
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/callback/impl/CallbackServiceImpl.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.callback.impl;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.examples.callback.api.CallbackListener;
+import com.alibaba.dubbo.examples.callback.api.CallbackService;
+
+/**
+ * CallbackServiceImpl
+ *
+ * @author william.liangf
+ */
+public class CallbackServiceImpl implements CallbackService {
+
+ private final Map<String, CallbackListener> listeners = new ConcurrentHashMap<String, CallbackListener>();
+
+ public CallbackServiceImpl() {
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ while(true) {
+ try {
+ for(Map.Entry<String, CallbackListener> entry : listeners.entrySet()){
+ try {
+ entry.getValue().changed(getChanged(entry.getKey()));
+ } catch (Throwable t) {
+ listeners.remove(entry.getKey());
+ }
+ }
+ Thread.sleep(5000); // 定时触发变更通知
+ } catch (Throwable t) { // 防御容错
+ t.printStackTrace();
+ }
+ }
+ }
+ });
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public void addListener(String key, CallbackListener listener) {
+ listeners.put(key, listener);
+ listener.changed(getChanged(key)); // 发送变更通知
+ }
+
+ private String getChanged(String key) {
+ return "Changed: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericConsumer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericConsumer.java
new file mode 100644
index 0000000..0d95d5b
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericConsumer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.generic;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.generic.api.IUserService;
+import com.alibaba.dubbo.examples.generic.api.IUserService.Params;
+import com.alibaba.dubbo.examples.generic.api.IUserService.User;
+
+/**
+ * GenericConsumer
+ *
+ * @author chao.liuc
+ */
+public class GenericConsumer {
+
+ public static void main(String[] args) throws Exception {
+ String config = GenericConsumer.class.getPackage().getName().replace('.', '/') + "/generic-consumer.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ IUserService userservice = (IUserService) context.getBean("userservice");
+ User user = userservice.get(new Params("a=b"));
+ System.out.println(user);
+ System.in.read();
+ }
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericProvider.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericProvider.java
new file mode 100644
index 0000000..0fc38f9
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/GenericProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.generic;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * GenericProvider
+ *
+ * @author chao.liuc
+ */
+public class GenericProvider {
+
+ public static void main(String[] args) throws Exception {
+ String config = GenericProvider.class.getPackage().getName().replace('.', '/') + "/generic-provider.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IService.java
new file mode 100644
index 0000000..3d1f3e3
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IService.java
@@ -0,0 +1,5 @@
+package com.alibaba.dubbo.examples.generic.api;
+
+public interface IService <P, V> {
+ V get(P params);
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IUserService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IUserService.java
new file mode 100644
index 0000000..09a855a
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/api/IUserService.java
@@ -0,0 +1,62 @@
+/**
+ * Project: dubbo-examples
+ *
+ * File Created at 2012-2-17
+ * $Id$
+ *
+ * Copyright 1999-2100 Alibaba.com Corporation Limited.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Alibaba Company. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Alibaba.com.
+ */
+package com.alibaba.dubbo.examples.generic.api;
+
+import java.io.Serializable;
+
+import com.alibaba.dubbo.examples.generic.api.IUserService.Params;
+import com.alibaba.dubbo.examples.generic.api.IUserService.User;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public interface IUserService extends IService<Params, User> {
+
+
+ public static class Params implements Serializable{
+ private static final long serialVersionUID = 1L;
+
+ public Params(String query) {
+ }
+ }
+ public static class User implements Serializable{
+ private static final long serialVersionUID = 1L;
+ public User(int id, String name) {
+ super();
+ this.id = id;
+ this.name = name;
+ }
+ private int id;
+ private String name;
+ public int getId() {
+ return id;
+ }
+ public void setId(int id) {
+ this.id = id;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ @Override
+ public String toString() {
+ return "User [id=" + id + ", name=" + name + "]";
+ }
+ }
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-consumer.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-consumer.xml
new file mode 100644
index 0000000..4f4b387
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="generic-consumer" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:reference id="userservice" interface="com.alibaba.dubbo.examples.generic.api.IUserService" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-provider.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-provider.xml
new file mode 100644
index 0000000..efddb37
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/generic-provider.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="generic-generic" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <bean id="userserviceimpl" class="com.alibaba.dubbo.examples.generic.impl.UserServiceImpl" />
+
+ <dubbo:service interface="com.alibaba.dubbo.examples.generic.api.IUserService" ref="userserviceimpl" >
+ </dubbo:service>
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/impl/UserServiceImpl.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/impl/UserServiceImpl.java
new file mode 100644
index 0000000..31329b1
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/generic/impl/UserServiceImpl.java
@@ -0,0 +1,29 @@
+/**
+ * Project: dubbo-examples
+ *
+ * File Created at 2012-2-17
+ * $Id$
+ *
+ * Copyright 1999-2100 Alibaba.com Corporation Limited.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Alibaba Company. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Alibaba.com.
+ */
+package com.alibaba.dubbo.examples.generic.impl;
+
+import com.alibaba.dubbo.examples.generic.api.IUserService;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class UserServiceImpl implements IUserService {
+
+ public User get(Params params) {
+ return new User(1, "charles");
+ }
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartBeatExchangeHandler.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartBeatExchangeHandler.java
new file mode 100644
index 0000000..016fc87
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartBeatExchangeHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.examples.heartbeat;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartBeatExchangeHandler extends HeaderExchangeHandler {
+
+ private AtomicInteger heartBeatCounter = new AtomicInteger( 0 );
+
+ public HeartBeatExchangeHandler( ExchangeHandler handler ) {
+ super( handler );
+ }
+
+ @Override
+ public void received( Channel channel, Object message ) throws RemotingException {
+ if ( message instanceof Request ) {
+ Request req = ( Request ) message;
+ if ( req.isHeartbeat() ) {
+ heartBeatCounter.incrementAndGet();
+ channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
+ Response res = new Response( req.getId(), req.getVersion() );
+ res.setEvent( req.getData() == null ? null : req.getData().toString() );
+ channel.send( res );
+ }
+ } else {
+ super.received( channel, message );
+ }
+ }
+
+ public int getHeartBeatCount() {
+ return heartBeatCounter.get();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatClient.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatClient.java
new file mode 100644
index 0000000..7703223
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatClient.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.examples.heartbeat;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Transporters;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartbeatClient {
+
+ private static final URL serverUrl = URL.valueOf(
+ new StringBuilder( 32 )
+ .append( "netty://" )
+ .append( NetUtils.getLocalHost() )
+ .append( ":9999" ).toString() )
+ .addParameter( Constants.CODEC_KEY, "exchange" );
+
+ private static final ExchangeHandler handler = new ExchangeHandlerAdapter() {
+
+ };
+
+ private static ExchangeServer exchangeServer;
+
+ private static volatile boolean serverStarted = false;
+
+ public static void main( String[] args ) throws Exception {
+
+ final HeartBeatExchangeHandler serverHandler = new HeartBeatExchangeHandler( handler );
+
+ Thread serverThread = new Thread( new Runnable() {
+
+ public void run() {
+ try {
+ exchangeServer = new HeaderExchangeServer(
+ Transporters.bind( serverUrl, serverHandler ) );
+ serverStarted = true;
+ } catch ( Exception e ) {
+ e.printStackTrace();
+ }
+ }
+ } );
+
+ serverThread.setDaemon( true );
+ serverThread.start();
+
+ while ( ! serverStarted ) {
+ Thread.sleep( 1000 );
+ }
+
+ URL url = serverUrl.addParameter( Constants.HEARTBEAT_KEY, 1000 );
+
+ HeartBeatExchangeHandler clientHandler = new HeartBeatExchangeHandler( handler );
+ ExchangeClient exchangeClient = new HeaderExchangeClient(
+ Transporters.connect( url, clientHandler ) );
+
+ for( int i = 0; i < 10; i++ ) {
+ Thread.sleep( 1000 );
+ System.out.print( "." );
+ }
+
+ System.out.println();
+
+ if ( serverHandler.getHeartBeatCount() > 0 ) {
+ System.out.printf( "Server receives %d heartbeats",
+ serverHandler.getHeartBeatCount() );
+ } else {
+ throw new Exception( "Client heartbeat does not work." );
+ }
+
+ exchangeClient.close();
+ exchangeServer.close();
+
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatConsumer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatConsumer.java
new file mode 100644
index 0000000..229ecf3
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatConsumer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.examples.heartbeat;
+
+import com.alibaba.dubbo.examples.heartbeat.api.HelloService;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartbeatConsumer {
+
+ public static void main(String[] args) throws Exception {
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
+ HeartbeatConsumer.class.getPackage().getName().replace('.', '/') + "/heartbeat-consumer.xml");
+ context.start();
+ HelloService hello = (HelloService) context.getBean("helloService");
+ for (int i = 0; i < Integer.MAX_VALUE; i++) {
+ System.out.println(hello.sayHello("kimi-" + i));
+ Thread.sleep(10000);
+ }
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatProvider.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatProvider.java
new file mode 100644
index 0000000..cec2ab9
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.examples.heartbeat;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartbeatProvider {
+
+ public static void main(String[] args) throws Exception {
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
+ HeartbeatProvider.class.getPackage().getName().replace( '.', '/' ) + "/heartbeat-provider.xml" );
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatServer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatServer.java
new file mode 100644
index 0000000..2792add
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/HeartbeatServer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.examples.heartbeat;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Transporters;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartbeatServer {
+
+ private static final URL clientUrl = URL.valueOf(
+ new StringBuilder( 32 )
+ .append( "netty://" )
+ .append( NetUtils.getLocalHost() )
+ .append( ":9999" ).toString() )
+ .addParameter( Constants.CODEC_KEY, "exchange" );
+
+ private static final ExchangeHandler handler = new ExchangeHandlerAdapter() {
+
+ };
+
+ private static ExchangeServer exchangeServer;
+
+ private static volatile boolean serverStarted = false;
+
+ public static void main( String[] args ) throws Exception {
+
+ final HeartBeatExchangeHandler serverHandler = new HeartBeatExchangeHandler( handler );
+
+ Thread serverThread = new Thread( new Runnable() {
+
+ public void run() {
+ try {
+ exchangeServer = new HeaderExchangeServer(
+ Transporters.bind(
+ clientUrl.addParameter( Constants.HEARTBEAT_KEY, 1000 ),
+ serverHandler ) );
+ serverStarted = true;
+ } catch ( Exception e ) {
+ e.printStackTrace();
+ }
+ }
+ } );
+
+ serverThread.setDaemon( true );
+ serverThread.start();
+
+ while ( !serverStarted ) {
+ Thread.sleep( 1000 );
+ }
+
+ HeartBeatExchangeHandler clientHandler = new HeartBeatExchangeHandler( handler );
+ ExchangeClient exchangeClient = new HeaderExchangeClient(
+ Transporters.connect( clientUrl, clientHandler ) );
+
+ for ( int i = 0; i < 10; i++ ) {
+ Thread.sleep( 1000 );
+ System.out.print( "." );
+ }
+
+ System.out.println();
+
+ if ( clientHandler.getHeartBeatCount() > 0 ) {
+ System.out.printf( "Client receives %d heartbeats",
+ clientHandler.getHeartBeatCount() );
+ } else {
+ throw new Exception( "Server heartbeat does not work." );
+ }
+
+ exchangeClient.close();
+ exchangeServer.close();
+
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/api/HelloService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/api/HelloService.java
new file mode 100644
index 0000000..6da3324
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/api/HelloService.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.examples.heartbeat.api;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public interface HelloService {
+
+ public String sayHello(String name);
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/heartbeat-consumer.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/heartbeat-consumer.xml
new file mode 100644
index 0000000..6d293bc
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/heartbeat-consumer.xml
@@ -0,0 +1,30 @@
+<!--
+ - Copyright 1999-2012 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="heartbeat-consumer"/>
+
+ <!--<dubbo:registry address="127.0.0.1:9090"/>-->
+
+ <dubbo:reference id="helloService" interface="com.alibaba.dubbo.examples.remoting.heartbeat.api.HelloService" url="dubbo://127.0.0.1:20880">
+ <dubbo:parameter key="heartbeat" value="3000" />
+ </dubbo:reference>
+
+</beans>
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/heartbeat-provider.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/heartbeat-provider.xml
new file mode 100644
index 0000000..49577bd
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/heartbeat-provider.xml
@@ -0,0 +1,34 @@
+<!--
+ - Copyright 1999-2012 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="heartbeat-provider"/>
+
+ <!--<dubbo:registry address="127.0.0.1:9090"/>-->
+
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <bean id="helloService" class="com.alibaba.dubbo.examples.remoting.heartbeat.impl.HelloServiceImpl"/>
+
+ <dubbo:service registry="N/A" interface="com.alibaba.dubbo.examples.remoting.heartbeat.api.HelloService" ref="helloService">
+ <dubbo:parameter key="heartbeat" value="3000" />
+ </dubbo:service>
+
+</beans>
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/impl/HelloServiceImpl.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/impl/HelloServiceImpl.java
new file mode 100644
index 0000000..a7f4cf6
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/heartbeat/impl/HelloServiceImpl.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.examples.heartbeat.impl;
+
+import com.alibaba.dubbo.examples.heartbeat.api.HelloService;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HelloServiceImpl implements HelloService {
+
+ public String sayHello(String name) {
+ return new StringBuilder(32).append("Hello, ").append(name).append("!").toString();
+ }
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer.java
new file mode 100644
index 0000000..e67024d
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.merge;
+
+import java.util.List;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.merge.api.MergeService;
+
+/**
+ * MergeConsumer
+ *
+ * @author william.liangf
+ */
+public class MergeConsumer {
+
+ public static void main(String[] args) throws Exception {
+ String config = MergeConsumer.class.getPackage().getName().replace('.', '/') + "/merge-consumer.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ MergeService mergeService = (MergeService)context.getBean("mergeService");
+ for (int i = 0; i < Integer.MAX_VALUE; i ++) {
+ try {
+ List<String> result = mergeService.mergeResult();
+ System.out.println("(" + i + ") " + result);
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer2.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer2.java
new file mode 100644
index 0000000..ca5016a
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeConsumer2.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.merge;
+
+import java.util.List;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.merge.api.MergeService;
+
+/**
+ * MergeConsumer2
+ *
+ * @author william.liangf
+ */
+public class MergeConsumer2 {
+
+ public static void main(String[] args) throws Exception {
+ String config = MergeConsumer2.class.getPackage().getName().replace('.', '/') + "/merge-consumer2.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ MergeService mergeService = (MergeService)context.getBean("mergeService");
+ for (int i = 0; i < Integer.MAX_VALUE; i ++) {
+ try {
+ List<String> result = mergeService.mergeResult();
+ System.out.println("(" + i + ") " + result);
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider.java
new file mode 100644
index 0000000..ee49b69
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.merge;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * MergeProvider
+ *
+ * @author william.liangf
+ */
+public class MergeProvider {
+
+ public static void main(String[] args) throws Exception {
+ String config = MergeProvider.class.getPackage().getName().replace('.', '/') + "/merge-provider.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider2.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider2.java
new file mode 100644
index 0000000..bf1bd80
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/MergeProvider2.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.merge;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * MergeProvider2
+ *
+ * @author william.liangf
+ */
+public class MergeProvider2 {
+
+ public static void main(String[] args) throws Exception {
+ String config = MergeProvider2.class.getPackage().getName().replace('.', '/') + "/merge-provider2.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/api/MergeService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/api/MergeService.java
new file mode 100644
index 0000000..eb8197f
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/api/MergeService.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.merge.api;
+
+import java.util.List;
+
+/**
+ * MergeService
+ *
+ * @author william.liangf
+ */
+public interface MergeService {
+
+ List<String> mergeResult();
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl.java
new file mode 100644
index 0000000..c1e5611
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.merge.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.examples.merge.api.MergeService;
+
+/**
+ * MenuServiceImpl
+ *
+ * @author william.liangf
+ */
+public class MergeServiceImpl implements MergeService {
+
+ public List<String> mergeResult() {
+ List<String> menus = new ArrayList<String>();
+ menus.add("group-1.1");
+ menus.add("group-1.2");
+ return menus;
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl2.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl2.java
new file mode 100644
index 0000000..a8f2c06
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl2.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.merge.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.examples.merge.api.MergeService;
+
+/**
+ * MenuServiceImpl
+ *
+ * @author william.liangf
+ */
+public class MergeServiceImpl2 implements MergeService {
+
+ public List<String> mergeResult() {
+ List<String> menus = new ArrayList<String>();
+ menus.add("group-2.1");
+ menus.add("group-2.2");
+ return menus;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl3.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl3.java
new file mode 100644
index 0000000..3185da8
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/impl/MergeServiceImpl3.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.merge.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.examples.merge.api.MergeService;
+
+/**
+ * MenuServiceImpl
+ *
+ * @author william.liangf
+ */
+public class MergeServiceImpl3 implements MergeService {
+
+ public List<String> mergeResult() {
+ List<String> menus = new ArrayList<String>();
+ menus.add("group-3.1");
+ menus.add("group-3.2");
+ return menus;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer.xml
new file mode 100644
index 0000000..05f7339
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="merge-consumer" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:reference id="mergeService" interface="com.alibaba.dubbo.examples.merge.api.MergeService" group="*" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer2.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer2.xml
new file mode 100644
index 0000000..d9673db
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-consumer2.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="merge-consumer2" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:reference id="mergeService" interface="com.alibaba.dubbo.examples.merge.api.MergeService" group="merge2,merge3" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider.xml
new file mode 100644
index 0000000..bdcaa71
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="merge-provider" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <bean id="mergeService" class="com.alibaba.dubbo.examples.merge.impl.MergeServiceImpl" />
+
+ <dubbo:service group="merge" interface="com.alibaba.dubbo.examples.merge.api.MergeService" ref="mergeService" />
+
+ <bean id="mergeService2" class="com.alibaba.dubbo.examples.merge.impl.MergeServiceImpl2" />
+
+ <dubbo:service group="merge2" interface="com.alibaba.dubbo.examples.merge.api.MergeService" ref="mergeService2" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider2.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider2.xml
new file mode 100644
index 0000000..b525e24
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/merge/merge-provider2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="merge-provider2" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20881" />
+
+ <bean id="mergeService3" class="com.alibaba.dubbo.examples.merge.impl.MergeServiceImpl3" />
+
+ <dubbo:service group="merge3" interface="com.alibaba.dubbo.examples.merge.api.MergeService" ref="mergeService3" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationConsumer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationConsumer.java
new file mode 100644
index 0000000..6333638
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationConsumer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.validation;
+
+import java.util.Date;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.validation.api.ValidationParameter;
+import com.alibaba.dubbo.examples.validation.api.ValidationService;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * ValidationConsumer
+ *
+ * @author william.liangf
+ */
+public class ValidationConsumer {
+
+ public static void main(String[] args) throws Exception {
+ String config = ValidationConsumer.class.getPackage().getName().replace('.', '/') + "/validation-consumer.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+
+ ValidationService validationService = (ValidationService)context.getBean("validationService");
+
+ // Save OK
+ ValidationParameter parameter = new ValidationParameter();
+ parameter.setName("liangfei");
+ parameter.setEmail("liangfei@liang.fei");
+ parameter.setAge(50);
+ parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));
+ parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));
+ validationService.save(parameter);
+ System.out.println("Validation Save OK");
+
+ // Save Error
+ try {
+ parameter = new ValidationParameter();
+ validationService.save(parameter);
+ System.err.println("Validation Save ERROR");
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ System.out.println(violations);
+ }
+
+ // Delete OK
+ validationService.delete(2, "abc");
+ System.out.println("Validation Delete OK");
+
+ // Delete Error
+ try {
+ validationService.delete(0, "abc");
+ System.err.println("Validation Delete ERROR");
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ System.out.println(violations);
+ }
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationProvider.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationProvider.java
new file mode 100644
index 0000000..a20f247
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/ValidationProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.validation;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * ValidationProvider
+ *
+ * @author william.liangf
+ */
+public class ValidationProvider {
+
+ public static void main(String[] args) throws Exception {
+ String config = ValidationProvider.class.getPackage().getName().replace('.', '/') + "/validation-provider.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationParameter.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationParameter.java
new file mode 100644
index 0000000..fea9dc6
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationParameter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.validation.api;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.validation.constraints.Future;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Past;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+/**
+ * ValidationParameter
+ *
+ * @author william.liangf
+ */
+public class ValidationParameter implements Serializable {
+
+ private static final long serialVersionUID = 7158911668568000392L;
+
+ @NotNull // 不允许为空
+ @Size(min = 2, max = 20) // 长度或大小范围
+ private String name;
+
+ @NotNull(groups = ValidationService.Save.class) // 保存时不允许为空,更新时允许为空 ,表示不更新该字段
+ @Pattern(regexp = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$")
+ private String email;
+
+ @Min(18) // 最小值
+ @Max(100) // 最大值
+ private int age;
+
+ @Past // 必须为一个过去的时间
+ private Date loginDate;
+
+ @Future // 必须为一个未来的时间
+ private Date expiryDate;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public Date getLoginDate() {
+ return loginDate;
+ }
+
+ public void setLoginDate(Date loginDate) {
+ this.loginDate = loginDate;
+ }
+
+ public Date getExpiryDate() {
+ return expiryDate;
+ }
+
+ public void setExpiryDate(Date expiryDate) {
+ this.expiryDate = expiryDate;
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationService.java
new file mode 100644
index 0000000..73e24fd
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/api/ValidationService.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.validation.api;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+
+/**
+ * ValidationService
+ *
+ * @author william.liangf
+ */
+public interface ValidationService { // 缺省可按服务接口区分验证场景,如:@NotNull(groups = ValidationService.class)
+
+ @interface Save{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Save.class),可选
+ void save(ValidationParameter parameter);
+
+ @interface Update{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Update.class),可选
+ void update(ValidationParameter parameter);
+
+ void delete(@Min(1) long id, @NotNull @Size(min = 2, max = 16) @Pattern(regexp = "^[a-zA-Z]+$") String operator);
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/impl/ValidationServiceImpl.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/impl/ValidationServiceImpl.java
new file mode 100644
index 0000000..f420aaf
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/impl/ValidationServiceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.validation.impl;
+
+import com.alibaba.dubbo.examples.validation.api.ValidationParameter;
+import com.alibaba.dubbo.examples.validation.api.ValidationService;
+
+/**
+ * ValidationServiceImpl
+ *
+ * @author william.liangf
+ */
+public class ValidationServiceImpl implements ValidationService {
+
+ public void save(ValidationParameter parameter) {
+ }
+
+ public void update(ValidationParameter parameter) {
+ }
+
+ public void delete(long id, String operator) {
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-consumer.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-consumer.xml
new file mode 100644
index 0000000..0e437e6
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="validation-consumer" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:reference id="validationService" interface="com.alibaba.dubbo.examples.validation.api.ValidationService" validation="true" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-provider.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-provider.xml
new file mode 100644
index 0000000..cf92eb5
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/validation/validation-provider.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="validation-provider" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <bean id="validationService" class="com.alibaba.dubbo.examples.validation.impl.ValidationServiceImpl" />
+
+ <dubbo:service interface="com.alibaba.dubbo.examples.validation.api.ValidationService" ref="validationService" validation="true" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/VersionConsumer.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/VersionConsumer.java
new file mode 100644
index 0000000..f0afd80
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/VersionConsumer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.version;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.version.api.VersionService;
+
+/**
+ * VersionConsumer
+ *
+ * @author william.liangf
+ */
+public class VersionConsumer {
+
+ public static void main(String[] args) throws Exception {
+ String config = VersionConsumer.class.getPackage().getName().replace('.', '/') + "/version-consumer.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ VersionService versionService = (VersionService) context.getBean("versionService");
+ for (int i = 0; i < 10000; i ++) {
+ String hello = versionService.sayHello("world");
+ System.out.println(hello);
+ Thread.sleep(2000);
+ }
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/VersionProvider.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/VersionProvider.java
new file mode 100644
index 0000000..5050ca9
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/VersionProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.version;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * VersionProvider
+ *
+ * @author william.liangf
+ */
+public class VersionProvider {
+
+ public static void main(String[] args) throws Exception {
+ String config = VersionProvider.class.getPackage().getName().replace('.', '/') + "/version-provider.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/VersionProvider2.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/VersionProvider2.java
new file mode 100644
index 0000000..be253a5
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/VersionProvider2.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.version;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * VersionProvider2
+ *
+ * @author william.liangf
+ */
+public class VersionProvider2 {
+
+ public static void main(String[] args) throws Exception {
+ String config = VersionProvider2.class.getPackage().getName().replace('.', '/') + "/version-provider2.xml";
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
+ context.start();
+ System.in.read();
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/api/VersionService.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/api/VersionService.java
new file mode 100644
index 0000000..c3d28f0
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/api/VersionService.java
@@ -0,0 +1,25 @@
+/**
+ * Project: dubbo-examples
+ *
+ * File Created at 2012-2-17
+ * $Id$
+ *
+ * Copyright 1999-2100 Alibaba.com Corporation Limited.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Alibaba Company. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Alibaba.com.
+ */
+package com.alibaba.dubbo.examples.version.api;
+
+/**
+ * @author william.liangf
+ */
+public interface VersionService {
+
+ String sayHello(String name);
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/impl/VersionServiceImpl.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/impl/VersionServiceImpl.java
new file mode 100644
index 0000000..52542a0
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/impl/VersionServiceImpl.java
@@ -0,0 +1,29 @@
+/**
+ * Project: dubbo-examples
+ *
+ * File Created at 2012-2-17
+ * $Id$
+ *
+ * Copyright 1999-2100 Alibaba.com Corporation Limited.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Alibaba Company. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Alibaba.com.
+ */
+package com.alibaba.dubbo.examples.version.impl;
+
+import com.alibaba.dubbo.examples.version.api.VersionService;
+
+/**
+ * @author william.liangf
+ */
+public class VersionServiceImpl implements VersionService {
+
+ public String sayHello(String name) {
+ return "hello, " + name;
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/impl/VersionServiceImpl2.java b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/impl/VersionServiceImpl2.java
new file mode 100644
index 0000000..86b7c22
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/impl/VersionServiceImpl2.java
@@ -0,0 +1,29 @@
+/**
+ * Project: dubbo-examples
+ *
+ * File Created at 2012-2-17
+ * $Id$
+ *
+ * Copyright 1999-2100 Alibaba.com Corporation Limited.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Alibaba Company. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Alibaba.com.
+ */
+package com.alibaba.dubbo.examples.version.impl;
+
+import com.alibaba.dubbo.examples.version.api.VersionService;
+
+/**
+ * @author william.liangf
+ */
+public class VersionServiceImpl2 implements VersionService {
+
+ public String sayHello(String name) {
+ return "hello2, " + name;
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/version-consumer.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/version-consumer.xml
new file mode 100644
index 0000000..1687f84
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/version-consumer.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="version-consumer" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:reference id="versionService" interface="com.alibaba.dubbo.examples.version.api.VersionService" version="*" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/version-provider.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/version-provider.xml
new file mode 100644
index 0000000..c8b2e88
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/version-provider.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="version-provider" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20880" />
+
+ <bean id="versionService" class="com.alibaba.dubbo.examples.version.impl.VersionServiceImpl" />
+
+ <dubbo:service interface="com.alibaba.dubbo.examples.version.api.VersionService" version="1.0.0" ref="versionService" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/version-provider2.xml b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/version-provider2.xml
new file mode 100644
index 0000000..be829bb
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/java/com/alibaba/dubbo/examples/version/version-provider2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="version-provider2" />
+
+ <dubbo:registry address="multicast://224.5.6.7:1234" />
+
+ <dubbo:protocol name="dubbo" port="20882" />
+
+ <bean id="versionService" class="com.alibaba.dubbo.examples.version.impl.VersionServiceImpl2" />
+
+ <dubbo:service interface="com.alibaba.dubbo.examples.version.api.VersionService" version="2.0.0" ref="versionService" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-examples/src/main/resources/log4j.xml b/dubbo-demo/dubbo-demo-examples/src/main/resources/log4j.xml
new file mode 100644
index 0000000..69ed180
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/main/resources/log4j.xml
@@ -0,0 +1,27 @@
+<!--
+ - Copyright 1999-2012 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+ <appender name="DUBBO" class="com.alibaba.dubbo.common.utils.DubboAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n"/>
+ </layout>
+ </appender>
+ <root>
+ <level value="INFO"/>
+ <appender-ref ref="DUBBO"/>
+ </root>
+</log4j:configuration>
diff --git a/dubbo-demo/dubbo-demo-examples/src/test/java/com/alibaba/dubbo/examples/annotation/AnnotationTest.java b/dubbo-demo/dubbo-demo-examples/src/test/java/com/alibaba/dubbo/examples/annotation/AnnotationTest.java
new file mode 100644
index 0000000..8a5c478
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/test/java/com/alibaba/dubbo/examples/annotation/AnnotationTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.annotation;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.annotation.action.AnnotationAction;
+
+/**
+ * AnnotationTest
+ *
+ * @author william.liangf
+ */
+public class AnnotationTest {
+
+ @Test
+ public void testAnnotation() {
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(AnnotationTest.class.getPackage().getName().replace('.', '/') + "/annotation-provider.xml");
+ providerContext.start();
+ try {
+ ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext(AnnotationTest.class.getPackage().getName().replace('.', '/') + "/annotation-consumer.xml");
+ consumerContext.start();
+ try {
+ AnnotationAction annotationAction = (AnnotationAction) consumerContext.getBean("annotationAction");
+ String hello = annotationAction.doSayHello("world");
+ assertEquals("annotation: hello, world", hello);
+ } finally {
+ consumerContext.stop();
+ consumerContext.close();
+ }
+ } finally {
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-examples/src/test/java/com/alibaba/dubbo/examples/validation/ValidationTest.java b/dubbo-demo/dubbo-demo-examples/src/test/java/com/alibaba/dubbo/examples/validation/ValidationTest.java
new file mode 100644
index 0000000..34c3ab2
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-examples/src/test/java/com/alibaba/dubbo/examples/validation/ValidationTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.examples.validation;
+
+import java.util.Date;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.examples.validation.api.ValidationParameter;
+import com.alibaba.dubbo.examples.validation.api.ValidationService;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * ValidationTest
+ *
+ * @author william.liangf
+ */
+public class ValidationTest {
+
+ @Test
+ public void testValidation() {
+ ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(ValidationTest.class.getPackage().getName().replace('.', '/') + "/validation-provider.xml");
+ providerContext.start();
+ try {
+ ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext(ValidationTest.class.getPackage().getName().replace('.', '/') + "/validation-consumer.xml");
+ consumerContext.start();
+ try {
+ ValidationService validationService = (ValidationService) consumerContext.getBean("validationService");
+
+ // Save OK
+ ValidationParameter parameter = new ValidationParameter();
+ parameter.setName("liangfei");
+ parameter.setEmail("liangfei@liang.fei");
+ parameter.setAge(50);
+ parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));
+ parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));
+ validationService.save(parameter);
+
+ try {
+ parameter = new ValidationParameter();
+ parameter.setName("l");
+ parameter.setEmail("liangfei@liang.fei");
+ parameter.setAge(50);
+ parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));
+ parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));
+ validationService.save(parameter);
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ }
+
+ // Save Error
+ try {
+ parameter = new ValidationParameter();
+ validationService.save(parameter);
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ }
+
+ // Delete OK
+ validationService.delete(2, "abc");
+
+ // Delete Error
+ try {
+ validationService.delete(2, "a");
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ Assert.assertEquals(1, violations.size());
+ }
+
+ // Delete Error
+ try {
+ validationService.delete(0, "abc");
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ Assert.assertEquals(1, violations.size());
+ }
+ try {
+ validationService.delete(2, null);
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ Assert.assertEquals(1, violations.size());
+ }
+ try {
+ validationService.delete(0, null);
+ Assert.fail();
+ } catch (RpcException e) {
+ ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
+ Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
+ Assert.assertNotNull(violations);
+ Assert.assertEquals(2, violations.size());
+ }
+ } finally {
+ consumerContext.stop();
+ consumerContext.close();
+ }
+ } finally {
+ providerContext.stop();
+ providerContext.close();
+ }
+ }
+
+}
diff --git a/dubbo-demo/dubbo-demo-provider/dep.log b/dubbo-demo/dubbo-demo-provider/dep.log
new file mode 100644
index 0000000..c789e30
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-provider/dep.log
@@ -0,0 +1,61 @@
+[INFO] Scanning for projects...
+[INFO] snapshot com.alibaba:dubbo-parent:2.2.0-SNAPSHOT: checking for updates from b2b-internal-snapshots
+[INFO] snapshot com.alibaba:dubbo-parent:2.2.0-SNAPSHOT: checking for updates from b2b-shared-repository
+[INFO] ------------------------------------------------------------------------
+[INFO] Building dubbo-demo-provider
+[INFO] task-segment: [dependency:tree]
+[INFO] ------------------------------------------------------------------------
+[INFO] snapshot com.alibaba:dubbo-demo:2.2.0-SNAPSHOT: checking for updates from b2b-internal-snapshots
+[INFO] snapshot com.alibaba:dubbo-demo:2.2.0-SNAPSHOT: checking for updates from b2b-shared-repository
+[INFO] snapshot com.alibaba:dubbo-demo:2.2.0-SNAPSHOT: checking for updates from opensesame.snapshots
+[INFO] snapshot com.alibaba:dubbo:2.2.0-SNAPSHOT: checking for updates from b2b-internal-snapshots
+[INFO] snapshot com.alibaba:dubbo:2.2.0-SNAPSHOT: checking for updates from b2b-shared-repository
+[INFO] snapshot com.alibaba:dubbo:2.2.0-SNAPSHOT: checking for updates from opensesame.snapshots
+[WARNING] *** CHECKSUM FAILED - Checksum failed on download: local = '42e7df2282f92e69fae6722b3a179c8395ef107b'; remote = 'c37760db920c78e5f91fbcf7505e74b5eeebbf14' - RETRYING
+[WARNING] *** CHECKSUM FAILED - Checksum failed on download: local = '42e7df2282f92e69fae6722b3a179c8395ef107b'; remote = 'c37760db920c78e5f91fbcf7505e74b5eeebbf14' - IGNORING
+[INFO] [dependency:tree {execution: default-cli}]
+[INFO] com.alibaba:dubbo-demo-provider:jar:2.2.0-SNAPSHOT
+[INFO] +- com.alibaba:dubbo-demo:jar:2.2.0-SNAPSHOT:compile
+[INFO] +- com.alibaba:dubbo:jar:2.2.0-SNAPSHOT:compile
+[INFO] | +- log4j:log4j:jar:1.2.16:compile
+[INFO] | +- org.javassist:javassist:jar:3.15.0-GA:compile
+[INFO] | +- org.springframework:spring:jar:2.5.6.SEC03:compile
+[INFO] | +- commons-logging:commons-logging:jar:1.1.1:compile
+[INFO] | \- org.jboss.netty:netty:jar:3.2.5.Final:compile
+[INFO] +- org.mortbay.jetty:jetty:jar:6.1.26:compile
+[INFO] | +- org.mortbay.jetty:jetty-util:jar:6.1.26:compile
+[INFO] | \- org.mortbay.jetty:servlet-api:jar:2.5-20081211:compile
+[INFO] +- org.apache.zookeeper:zookeeper:jar:3.3.3:compile
+[INFO] | \- jline:jline:jar:0.9.94:compile
+[INFO] +- redis.clients:jedis:jar:2.0.0:compile
+[INFO] | \- commons-pool:commons-pool:jar:1.5.5:compile
+[INFO] +- org.apache.mina:mina-core:jar:1.1.7:compile
+[INFO] | \- org.slf4j:slf4j-api:jar:1.6.2:compile (version managed from 1.4.3)
+[INFO] +- org.glassfish.grizzly:grizzly-core:jar:2.1.4:compile
+[INFO] | +- org.glassfish.grizzly:grizzly-framework:jar:2.1.4:compile
+[INFO] | | \- org.glassfish.gmbal:gmbal-api-only:jar:3.0.0-b023:compile
+[INFO] | | \- org.glassfish.external:management-api:jar:3.0.0-b012:compile
+[INFO] | +- org.glassfish.grizzly:grizzly-portunif:jar:2.1.4:compile
+[INFO] | \- org.glassfish.grizzly:grizzly-rcm:jar:2.1.4:compile
+[INFO] +- org.apache.httpcomponents:httpclient:jar:4.1.2:compile
+[INFO] | +- org.apache.httpcomponents:httpcore:jar:4.1.2:compile
+[INFO] | \- commons-codec:commons-codec:jar:1.4:compile
+[INFO] +- com.caucho:hessian:jar:4.0.7:compile
+[INFO] +- com.alibaba:fastjson:jar:1.1.8:compile
+[INFO] +- javax.validation:validation-api:jar:1.0.0.GA:compile
+[INFO] +- org.hibernate:hibernate-validator:jar:4.2.0.Final:compile
+[INFO] +- javax.cache:cache-api:jar:0.4:compile
+[INFO] +- junit:junit:jar:4.10:test
+[INFO] | \- org.hamcrest:hamcrest-core:jar:1.1:test
+[INFO] +- org.easymock:easymock:jar:3.0:test
+[INFO] | +- cglib:cglib-nodep:jar:2.2:test
+[INFO] | \- org.objenesis:objenesis:jar:1.2:test
+[INFO] +- org.easymock:easymockclassextension:jar:3.0:test
+[INFO] \- com.googlecode.jmockit:jmockit:jar:0.999.8:test
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESSFUL
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time: 23 seconds
+[INFO] Finished at: Thu Mar 29 13:45:40 CST 2012
+[INFO] Final Memory: 17M/41M
+[INFO] ------------------------------------------------------------------------
diff --git a/dubbo-demo/dubbo-demo-provider/pom.xml b/dubbo-demo/dubbo-demo-provider/pom.xml
new file mode 100644
index 0000000..2ffcac8
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-provider/pom.xml
@@ -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.
+-->
+<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-demo</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-demo-provider</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The demo provider module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-demo-api</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>com.github.sgroschupf</groupId>
+ <artifactId>zkclient</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>redis.clients</groupId>
+ <artifactId>jedis</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>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.cache</groupId>
+ <artifactId>cache-api</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/dubbo-demo-provider/src/main/assembly/assembly.xml b/dubbo-demo/dubbo-demo-provider/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..25235d9
--- /dev/null
+++ b/dubbo-demo/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/dubbo-demo-provider/src/main/assembly/conf/dubbo.properties b/dubbo-demo/dubbo-demo-provider/src/main/assembly/conf/dubbo.properties
new file mode 100644
index 0000000..728be32
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-provider/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
+dubbo.application.name=demo-provider
+dubbo.application.owner=
+dubbo.registry.address=multicast://224.5.6.7:1234
+#dubbo.registry.address=zookeeper://127.0.0.1:2181
+#dubbo.registry.address=redis://127.0.0.1:6379
+#dubbo.registry.address=dubbo://127.0.0.1:9090
+dubbo.monitor.protocol=registry
+dubbo.protocol.name=dubbo
+dubbo.protocol.port=20880
+dubbo.service.loadbalance=roundrobin
+dubbo.log4j.file=logs/dubbo-demo-provider.log
+dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-provider/src/main/java/com/alibaba/dubbo/demo/provider/DemoServiceImpl.java b/dubbo-demo/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/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/dubbo-demo-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml b/dubbo-demo/dubbo-demo-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml
new file mode 100644
index 0000000..ea8026b
--- /dev/null
+++ b/dubbo-demo/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" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/DemoProvider.java b/dubbo-demo/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/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/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/ExitProvider.java b/dubbo-demo/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/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/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/MockAdd.java b/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/MockAdd.java
new file mode 100644
index 0000000..92147f6
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/MockAdd.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.demo.provider;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.demo.DemoService;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * RegistryContainer
+ *
+ * @author william.liangf
+ */
+public class MockAdd {
+
+ private static final Logger logger = LoggerFactory.getLogger(MockAdd.class);
+
+ public static final String REGISTRY_ADDRESS = "dubbo.registry.address";
+
+ private final RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
+
+ private Registry registry;
+
+ public Registry getRegistry() {
+ return registry;
+ }
+
+ public static void main(String[] args) throws Exception {
+ MockAdd c = new MockAdd();
+ c.start();
+ Thread.sleep(500);
+ c.stop();
+ }
+
+ 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).addParameter(Constants.CHECK_KEY, String.valueOf(false));
+ registry = registryFactory.getRegistry(registryUrl);
+ URL mockUrl = URL.valueOf("override://" + NetUtils.getLocalHost() + "/" + DemoService.class.getName() + "?category=configurators&dynamic=false&mock=force:return+null");
+ registry.register(mockUrl);
+ }
+
+ public void stop() {
+ try {
+ registry.destroy();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/MockRemove.java b/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/MockRemove.java
new file mode 100644
index 0000000..1140e1e
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/MockRemove.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.demo.provider;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.demo.DemoService;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * RegistryContainer
+ *
+ * @author william.liangf
+ */
+public class MockRemove {
+
+ private static final Logger logger = LoggerFactory.getLogger(MockRemove.class);
+
+ public static final String REGISTRY_ADDRESS = "dubbo.registry.address";
+
+ private final RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
+
+ private Registry registry;
+
+ public Registry getRegistry() {
+ return registry;
+ }
+
+ public static void main(String[] args) throws Exception {
+ MockRemove c = new MockRemove();
+ c.start();
+ Thread.sleep(500);
+ c.stop();
+ }
+
+ 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).addParameter(Constants.CHECK_KEY, String.valueOf(false));
+ registry = registryFactory.getRegistry(registryUrl);
+ URL mockUrl = URL.valueOf("override://" + NetUtils.getLocalHost() + "/" + DemoService.class.getName() + "?category=configurators&dynamic=false&mock=force:return+null");
+ registry.unregister(mockUrl);
+ }
+
+ public void stop() {
+ try {
+ registry.destroy();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/RouteAdd.java b/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/RouteAdd.java
new file mode 100644
index 0000000..5c62396
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/RouteAdd.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.demo.provider;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.demo.DemoService;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * RegistryContainer
+ *
+ * @author william.liangf
+ */
+public class RouteAdd {
+
+ private static final Logger logger = LoggerFactory.getLogger(RouteAdd.class);
+
+ public static final String REGISTRY_ADDRESS = "dubbo.registry.address";
+
+ private final RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
+
+ private Registry registry;
+
+ public Registry getRegistry() {
+ return registry;
+ }
+
+ public static void main(String[] args) throws Exception {
+ RouteAdd c = new RouteAdd();
+ c.start();
+ Thread.sleep(500);
+ c.stop();
+ }
+
+ 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).addParameter(Constants.CHECK_KEY, String.valueOf(false));
+ registry = registryFactory.getRegistry(registryUrl);
+ URL routeUrl = URL.valueOf("condition://" + NetUtils.getLocalHost() + "/" + DemoService.class.getName() + "?category=routers&dynamic=false&rule=" + URL.encode("host=" + NetUtils.getLocalHost() + " => host=" + NetUtils.getLocalHost()));
+ registry.register(routeUrl);
+ }
+
+ public void stop() {
+ try {
+ registry.destroy();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/RouteRemove.java b/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/RouteRemove.java
new file mode 100644
index 0000000..3a9a2c1
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-provider/src/test/java/com/alibaba/dubbo/demo/provider/RouteRemove.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.demo.provider;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.demo.DemoService;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * RegistryContainer
+ *
+ * @author william.liangf
+ */
+public class RouteRemove {
+
+ private static final Logger logger = LoggerFactory.getLogger(RouteRemove.class);
+
+ public static final String REGISTRY_ADDRESS = "dubbo.registry.address";
+
+ private final RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
+
+ private Registry registry;
+
+ public Registry getRegistry() {
+ return registry;
+ }
+
+ public static void main(String[] args) throws Exception {
+ RouteRemove c = new RouteRemove();
+ c.start();
+ Thread.sleep(500);
+ c.stop();
+ }
+
+ 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).addParameter(Constants.CHECK_KEY, String.valueOf(false));
+ registry = registryFactory.getRegistry(registryUrl);
+ URL routeUrl = URL.valueOf("condition://" + NetUtils.getLocalHost() + "/" + DemoService.class.getName() + "?category=routers&dynamic=false&rule=" + URL.encode("host=" + NetUtils.getLocalHost() + " => host=" + NetUtils.getLocalHost()));
+ registry.unregister(routeUrl);
+ }
+
+ public void stop() {
+ try {
+ registry.destroy();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-provider/src/test/resources/dubbo.properties b/dubbo-demo/dubbo-demo-provider/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..fb0e4ba
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-provider/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
+dubbo.application.name=demo-provider
+dubbo.application.owner=william
+dubbo.registry.address=multicast://224.5.6.7:1234
+#dubbo.registry.address=zookeeper://127.0.0.1:2181
+#dubbo.registry.address=redis://127.0.0.1:6379
+#dubbo.registry.address=dubbo://127.0.0.1:9090
+dubbo.monitor.protocol=registry
+dubbo.protocol.name=dubbo
+dubbo.protocol.port=-1
+dubbo.service.loadbalance=roundrobin
+#dubbo.log4j.file=logs/dubbo-demo-consumer.log
+#dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-demo/dubbo-demo-provider/src/test/resources/log4j.xml b/dubbo-demo/dubbo-demo-provider/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-demo/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..839e6a1
--- /dev/null
+++ b/dubbo-demo/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.2.2</version>
+ </parent>
+ <artifactId>dubbo-demo</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The demo module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-demo-api</module>
+ <module>dubbo-demo-provider</module>
+ <module>dubbo-demo-consumer</module>
+ <module>dubbo-demo-examples</module>
+ </modules>
+</project>
diff --git a/dubbo-filter/dubbo-filter-cache/pom.xml b/dubbo-filter/dubbo-filter-cache/pom.xml
new file mode 100644
index 0000000..5d971a4
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/pom.xml
@@ -0,0 +1,43 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-filter</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-filter-cache</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The cache module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.cache</groupId>
+ <artifactId>cache-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/Cache.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/Cache.java
new file mode 100644
index 0000000..61a0399
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/Cache.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache;
+
+/**
+ * Cache
+ *
+ * @author william.liangf
+ */
+public interface Cache {
+
+ void put(Object key, Object value);
+
+ Object get(Object key);
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/CacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/CacheFactory.java
new file mode 100644
index 0000000..0bdd471
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/CacheFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * CacheFactory
+ *
+ * @author william.liangf
+ */
+@SPI("lru")
+public interface CacheFactory {
+
+ @Adaptive("cache")
+ Cache getCache(URL url);
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java
new file mode 100644
index 0000000..4d4e4d3
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.filter;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.cache.CacheFactory;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * CacheFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY)
+public class CacheFilter implements Filter {
+
+ private CacheFactory cacheFactory;
+
+ public void setCacheFactory(CacheFactory cacheFactory) {
+ this.cacheFactory = cacheFactory;
+ }
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.CACHE_KEY))) {
+ Cache cache = cacheFactory.getCache(invoker.getUrl().addParameter(Constants.METHOD_KEY, invocation.getMethodName()));
+ if (cache != null) {
+ String key = StringUtils.toArgumentString(invocation.getArguments());
+ if (cache != null && key != null) {
+ Object value = cache.get(key);
+ if (value != null) {
+ return new RpcResult(value);
+ }
+ Result result = invoker.invoke(invocation);
+ if (! result.hasException()) {
+ cache.put(key, result.getValue());
+ }
+ return result;
+ }
+ }
+ }
+ return invoker.invoke(invocation);
+ }
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java
new file mode 100644
index 0000000..6c9e340
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.cache.CacheFactory;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * AbstractCacheFactory
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractCacheFactory implements CacheFactory {
+
+ private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
+
+ public Cache getCache(URL url) {
+ String key = url.toFullString();
+ Cache cache = caches.get(key);
+ if (cache == null) {
+ caches.put(key, createCache(url));
+ cache = caches.get(key);
+ }
+ return cache;
+ }
+
+ protected abstract Cache createCache(URL url);
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java
new file mode 100644
index 0000000..66fd5af
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.jcache;
+
+import javax.cache.Cache;
+import javax.cache.CacheBuilder;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * JCache
+ *
+ * @author william.liangf
+ */
+public class JCache implements com.alibaba.dubbo.cache.Cache {
+
+ private final Cache<Object, Object> store;
+
+ public JCache(URL url) {
+ String type = url.getParameter("jcache");
+ CacheManager cacheManager = type == null || type.length() == 0 ? Caching.getCacheManager() : Caching.getCacheManager(type);
+ CacheBuilder<Object, Object> cacheBuilder = cacheManager.createCacheBuilder(url.getServiceKey());
+ this.store = cacheBuilder.build();
+ }
+
+ public void put(Object key, Object value) {
+ store.put(key, value);
+ }
+
+ public Object get(Object key) {
+ return store.get(key);
+ }
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java
new file mode 100644
index 0000000..c5e0cfd
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.jcache;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.cache.support.AbstractCacheFactory;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * JCacheFactory
+ *
+ * @author william.liangf
+ */
+public class JCacheFactory extends AbstractCacheFactory {
+
+ protected Cache createCache(URL url) {
+ return new JCache(url);
+ }
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java
new file mode 100644
index 0000000..33609dc
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.lru;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * LruCache
+ *
+ * @author william.liangf
+ */
+public class LruCache implements Cache {
+
+ private final Map<Object, Object> store;
+
+ public LruCache(URL url) {
+ final int max = url.getParameter("cache.size", 1000);
+ this.store = new LinkedHashMap<Object, Object>() {
+ private static final long serialVersionUID = -3834209229668463829L;
+ @Override
+ protected boolean removeEldestEntry(Entry<Object, Object> eldest) {
+ return size() > max;
+ }
+ };
+ }
+
+ public void put(Object key, Object value) {
+ synchronized (store) {
+ store.put(key, value);
+ }
+ }
+
+ public Object get(Object key) {
+ synchronized (store) {
+ return store.get(key);
+ }
+ }
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java
new file mode 100644
index 0000000..0e48999
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.lru;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.cache.support.AbstractCacheFactory;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * LruCacheFactory
+ *
+ * @author william.liangf
+ */
+public class LruCacheFactory extends AbstractCacheFactory {
+
+ protected Cache createCache(URL url) {
+ return new LruCache(url);
+ }
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java
new file mode 100644
index 0000000..9532696
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.threadlocal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * ThreadLocalCache
+ *
+ * @author william.liangf
+ */
+public class ThreadLocalCache implements Cache {
+
+ private final ThreadLocal<Map<Object, Object>> store;
+
+ public ThreadLocalCache(URL url) {
+ this.store = new ThreadLocal<Map<Object, Object>>() {
+ @Override
+ protected Map<Object, Object> initialValue() {
+ return new HashMap<Object, Object>();
+ }
+ };
+ }
+
+ public void put(Object key, Object value) {
+ store.get().put(key, value);
+ }
+
+ public Object get(Object key) {
+ return store.get().get(key);
+ }
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java
new file mode 100644
index 0000000..ca7ace3
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.threadlocal;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.cache.support.AbstractCacheFactory;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * ThreadLocalCacheFactory
+ *
+ * @author william.liangf
+ */
+public class ThreadLocalCacheFactory extends AbstractCacheFactory {
+
+ protected Cache createCache(URL url) {
+ return new ThreadLocalCache(url);
+ }
+
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory
new file mode 100644
index 0000000..6e3ddd0
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory
@@ -0,0 +1,3 @@
+threadlocal=com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
+lru=com.alibaba.dubbo.cache.support.lru.LruCacheFactory
+jcache=com.alibaba.dubbo.cache.support.jcache.JCacheFactory
\ No newline at end of file
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..ec25092
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+cache=com.alibaba.dubbo.cache.filter.CacheFilter
\ No newline at end of file
diff --git a/dubbo-filter/dubbo-filter-validation/pom.xml b/dubbo-filter/dubbo-filter-validation/pom.xml
new file mode 100644
index 0000000..6b9d30d
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-validation/pom.xml
@@ -0,0 +1,43 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-filter</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-filter-validation</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The validation module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/Validation.java b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/Validation.java
new file mode 100644
index 0000000..107fd2c
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/Validation.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.validation;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * Validation
+ *
+ * @author william.liangf
+ */
+@SPI("jvalidation")
+public interface Validation {
+
+ @Adaptive(Constants.VALIDATION_KEY)
+ Validator getValidator(URL url);
+
+}
diff --git a/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/Validator.java b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/Validator.java
new file mode 100644
index 0000000..528e054
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/Validator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.validation;
+
+/**
+ * Validator
+ *
+ * @author william.liangf
+ */
+public interface Validator {
+
+ void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception;
+
+}
diff --git a/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/filter/ValidationFilter.java b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/filter/ValidationFilter.java
new file mode 100644
index 0000000..7896bcc
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/filter/ValidationFilter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.validation.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.validation.Validation;
+import com.alibaba.dubbo.validation.Validator;
+
+/**
+ * ValidationFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = { Constants.CONSUMER, Constants.PROVIDER }, value = Constants.VALIDATION_KEY, order = 10000)
+public class ValidationFilter implements Filter {
+
+ private Validation validation;
+
+ public void setValidation(Validation validation) {
+ this.validation = validation;
+ }
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ if (validation != null && ! invocation.getMethodName().startsWith("$")
+ && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.VALIDATION_KEY))) {
+ try {
+ Validator validator = validation.getValidator(invoker.getUrl());
+ if (validator != null) {
+ validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
+ }
+ } catch (RpcException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new RpcException(t.getMessage(), t);
+ }
+ }
+ return invoker.invoke(invocation);
+ }
+
+}
diff --git a/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/support/AbstractValidation.java b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/support/AbstractValidation.java
new file mode 100644
index 0000000..9a866c9
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/support/AbstractValidation.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.validation.support;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.validation.Validation;
+import com.alibaba.dubbo.validation.Validator;
+
+/**
+ * AbstractValidation
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractValidation implements Validation {
+
+ private final ConcurrentMap<String, Validator> validators = new ConcurrentHashMap<String, Validator>();
+
+ public Validator getValidator(URL url) {
+ String key = url.toFullString();
+ Validator validator = validators.get(key);
+ if (validator == null) {
+ validators.put(key, createValidator(url));
+ validator = validators.get(key);
+ }
+ return validator;
+ }
+
+ protected abstract Validator createValidator(URL url);
+
+}
diff --git a/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidation.java b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidation.java
new file mode 100644
index 0000000..06ec0a0
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidation.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.validation.support.jvalidation;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.validation.Validator;
+import com.alibaba.dubbo.validation.support.AbstractValidation;
+
+/**
+ * JValidation
+ *
+ * @author william.liangf
+ */
+public class JValidation extends AbstractValidation {
+
+ @Override
+ protected Validator createValidator(URL url) {
+ return new JValidator(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidator.java b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidator.java
new file mode 100644
index 0000000..1c84bd1
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-validation/src/main/java/com/alibaba/dubbo/validation/support/jvalidation/JValidator.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.validation.support.jvalidation;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtNewConstructor;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.annotation.ArrayMemberValue;
+import javassist.bytecode.annotation.BooleanMemberValue;
+import javassist.bytecode.annotation.ByteMemberValue;
+import javassist.bytecode.annotation.CharMemberValue;
+import javassist.bytecode.annotation.ClassMemberValue;
+import javassist.bytecode.annotation.DoubleMemberValue;
+import javassist.bytecode.annotation.EnumMemberValue;
+import javassist.bytecode.annotation.FloatMemberValue;
+import javassist.bytecode.annotation.IntegerMemberValue;
+import javassist.bytecode.annotation.LongMemberValue;
+import javassist.bytecode.annotation.MemberValue;
+import javassist.bytecode.annotation.ShortMemberValue;
+import javassist.bytecode.annotation.StringMemberValue;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validation;
+import javax.validation.ValidatorFactory;
+import javax.validation.groups.Default;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.bytecode.ClassGenerator;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.validation.Validator;
+
+/**
+ * JValidator
+ *
+ * @author william.liangf
+ */
+public class JValidator implements Validator {
+
+ private static final Logger logger = LoggerFactory.getLogger(JValidator.class);
+
+ private final Class<?> clazz;
+
+ private final javax.validation.Validator validator;
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public JValidator(URL url) {
+ this.clazz = ReflectUtils.forName(url.getServiceInterface());
+ String jvalidation = url.getParameter("jvalidation");
+ ValidatorFactory factory;
+ if (jvalidation != null && jvalidation.length() > 0) {
+ factory = Validation.byProvider((Class)ReflectUtils.forName(jvalidation)).configure().buildValidatorFactory();
+ } else {
+ factory = Validation.buildDefaultValidatorFactory();
+ }
+ this.validator = factory.getValidator();
+ }
+
+ public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
+ String methodClassName = clazz.getName() + "$" + toUpperMethoName(methodName);
+ Class<?> methodClass = null;
+ try {
+ methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader());
+ } catch (ClassNotFoundException e) {
+ }
+ Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>>();
+ Method method = clazz.getMethod(methodName, parameterTypes);
+ Object parameterBean = getMethodParameterBean(clazz, method, arguments);
+ if (parameterBean != null) {
+ if (methodClass != null) {
+ violations.addAll(validator.validate(parameterBean, Default.class, clazz, methodClass));
+ } else {
+ violations.addAll(validator.validate(parameterBean, Default.class, clazz));
+ }
+ }
+ for (Object arg : arguments) {
+ validate(violations, arg, clazz, methodClass);
+ }
+ if (violations.size() > 0) {
+ throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations);
+ }
+ }
+
+ private void validate(Set<ConstraintViolation<?>> violations, Object arg, Class<?> clazz, Class<?> methodClass) {
+ if (arg != null && ! isPrimitives(arg.getClass())) {
+ if (Object[].class.isInstance(arg)) {
+ for (Object item : (Object[]) arg) {
+ validate(violations, item, clazz, methodClass);
+ }
+ } else if (Collection.class.isInstance(arg)) {
+ for (Object item : (Collection<?>) arg) {
+ validate(violations, item, clazz, methodClass);
+ }
+ } else if (Map.class.isInstance(arg)) {
+ for (Map.Entry<?, ?> entry : ((Map<?, ?>) arg).entrySet()) {
+ validate(violations, entry.getKey(), clazz, methodClass);
+ validate(violations, entry.getValue(), clazz, methodClass);
+ }
+ } else {
+ if (methodClass != null) {
+ violations.addAll(validator.validate(arg, Default.class, clazz, methodClass));
+ } else {
+ violations.addAll(validator.validate(arg, Default.class, clazz));
+ }
+ }
+ }
+ }
+
+ private static boolean isPrimitives(Class<?> cls) {
+ if (cls.isArray()) {
+ return isPrimitive(cls.getComponentType());
+ }
+ return isPrimitive(cls);
+ }
+
+ private static boolean isPrimitive(Class<?> cls) {
+ return cls.isPrimitive() || cls == String.class || cls == Boolean.class || cls == Character.class
+ || Number.class.isAssignableFrom(cls) || Date.class.isAssignableFrom(cls);
+ }
+
+ private static Object getMethodParameterBean(Class<?> clazz, Method method, Object[] args) {
+ if (! hasConstraintParameter(method)) {
+ return null;
+ }
+ try {
+ String upperName = toUpperMethoName(method.getName());
+ String parameterSimpleName = upperName + "Parameter";
+ String parameterClassName = clazz.getName() + "$" + parameterSimpleName;
+ Class<?> parameterClass;
+ try {
+ parameterClass = (Class<?>) Class.forName(parameterClassName, true, clazz.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());
+ CtClass ctClass = pool.makeClass(parameterClassName);
+ ClassFile classFile = ctClass.getClassFile();
+ classFile.setVersionToJava5();
+ ctClass.addConstructor(CtNewConstructor.defaultConstructor(pool.getCtClass(parameterClassName)));
+ // parameter fields
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+ for (int i = 0; i < parameterTypes.length; i ++) {
+ Class<?> type = parameterTypes[i];
+ Annotation[] annotations = parameterAnnotations[i];
+ AnnotationsAttribute attribute = new AnnotationsAttribute(classFile.getConstPool(), AnnotationsAttribute.visibleTag);
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
+ javassist.bytecode.annotation.Annotation ja = new javassist.bytecode.annotation.Annotation(
+ classFile.getConstPool(), pool.getCtClass(annotation.annotationType().getName()));
+ Method[] members = annotation.annotationType().getMethods();
+ for (Method member : members) {
+ if (Modifier.isPublic(member.getModifiers())
+ && member.getParameterTypes().length == 0
+ && member.getDeclaringClass() == annotation.annotationType()) {
+ Object value = member.invoke(annotation, new Object[0]);
+ if (value != null && ! value.equals(member.getDefaultValue())) {
+ MemberValue memberValue = createMemberValue(
+ classFile.getConstPool(), pool.get(member.getReturnType().getName()), value);
+ ja.addMemberValue(member.getName(), memberValue);
+ }
+ }
+ }
+ attribute.addAnnotation(ja);
+ }
+ }
+ String fieldName = method.getName() + "Argument" + i;
+ CtField ctField = CtField.make("public " + type.getCanonicalName() + " " + fieldName + ";", pool.getCtClass(parameterClassName));
+ ctField.getFieldInfo().addAttribute(attribute);
+ ctClass.addField(ctField);
+ }
+ parameterClass = ctClass.toClass();
+ }
+ Object parameterBean = parameterClass.newInstance();
+ for (int i = 0; i < args.length; i ++) {
+ Field field = parameterClass.getField(method.getName() + "Argument" + i);
+ field.set(parameterBean, args[i]);
+ }
+ return parameterBean;
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ return null;
+ }
+ }
+
+ private static boolean hasConstraintParameter(Method method) {
+ Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+ if (parameterAnnotations != null && parameterAnnotations.length > 0) {
+ for (Annotation[] annotations : parameterAnnotations) {
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static String toUpperMethoName(String methodName) {
+ return methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
+ }
+
+ // Copy from javassist.bytecode.annotation.Annotation.createMemberValue(ConstPool, CtClass);
+ private static MemberValue createMemberValue(ConstPool cp, CtClass type, Object value) throws NotFoundException {
+ MemberValue memberValue = javassist.bytecode.annotation.Annotation.createMemberValue(cp, type);
+ if (memberValue instanceof BooleanMemberValue)
+ ((BooleanMemberValue) memberValue).setValue((Boolean) value);
+ else if (memberValue instanceof ByteMemberValue)
+ ((ByteMemberValue) memberValue).setValue((Byte) value);
+ else if (memberValue instanceof CharMemberValue)
+ ((CharMemberValue) memberValue).setValue((Character) value);
+ else if (memberValue instanceof ShortMemberValue)
+ ((ShortMemberValue) memberValue).setValue((Short) value);
+ else if (memberValue instanceof IntegerMemberValue)
+ ((IntegerMemberValue) memberValue).setValue((Integer) value);
+ else if (memberValue instanceof LongMemberValue)
+ ((LongMemberValue) memberValue).setValue((Long) value);
+ else if (memberValue instanceof FloatMemberValue)
+ ((FloatMemberValue) memberValue).setValue((Float) value);
+ else if (memberValue instanceof DoubleMemberValue)
+ ((DoubleMemberValue) memberValue).setValue((Double) value);
+ else if (memberValue instanceof ClassMemberValue)
+ ((ClassMemberValue) memberValue).setValue(((Class<?>)value).getName());
+ else if (memberValue instanceof StringMemberValue)
+ ((StringMemberValue) memberValue).setValue((String) value);
+ else if (memberValue instanceof EnumMemberValue)
+ ((EnumMemberValue) memberValue).setValue(((Enum<?>) value).name());
+ /* else if (memberValue instanceof AnnotationMemberValue) */
+ else if (memberValue instanceof ArrayMemberValue) {
+ CtClass arrayType = type.getComponentType();
+ int len = Array.getLength(value);
+ MemberValue[] members = new MemberValue[len];
+ for (int i = 0; i < len; i ++) {
+ members[i] = createMemberValue(cp, arrayType, Array.get(value, i));
+ }
+ ((ArrayMemberValue) memberValue).setValue(members);
+ }
+ return memberValue;
+ }
+
+}
diff --git a/dubbo-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..2e3e5b3
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+validation=com.alibaba.dubbo.validation.filter.ValidationFilter
\ No newline at end of file
diff --git a/dubbo-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.validation.Validation b/dubbo-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.validation.Validation
new file mode 100644
index 0000000..7f344d4
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.validation.Validation
@@ -0,0 +1 @@
+jvalidation=com.alibaba.dubbo.validation.support.jvalidation.JValidation
\ No newline at end of file
diff --git a/dubbo-filter/pom.xml b/dubbo-filter/pom.xml
new file mode 100644
index 0000000..c81bf03
--- /dev/null
+++ b/dubbo-filter/pom.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.
+-->
+<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.2.2</version>
+ </parent>
+ <artifactId>dubbo-filter</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The filter module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-filter-cache</module>
+ <module>dubbo-filter-validation</module>
+ </modules>
+</project>
diff --git a/dubbo-monitor/dubbo-monitor-api/pom.xml b/dubbo-monitor/dubbo-monitor-api/pom.xml
new file mode 100644
index 0000000..6c9aece
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-api/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-monitor</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-monitor-api</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/Monitor.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/Monitor.java
new file mode 100644
index 0000000..0c12a53
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-api/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/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java
new file mode 100644
index 0000000..d61fb01
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * MonitorFactory. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@SPI("dubbo")
+public interface MonitorFactory {
+
+ /**
+ * Create monitor.
+ *
+ * @param url
+ * @return monitor
+ */
+ @Adaptive("protocol")
+ Monitor getMonitor(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/MonitorService.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/MonitorService.java
new file mode 100644
index 0000000..be2b7c4
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-api/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";
+
+ /**
+ * collect.
+ *
+ * @param statistics
+ */
+ void collect(URL statistics);
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/support/AbstractMonitorFactroy.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/support/AbstractMonitorFactroy.java
new file mode 100644
index 0000000..82198da
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-api/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/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/support/MonitorFilter.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/support/MonitorFilter.java
new file mode 100644
index 0000000..238d9e9
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/com/alibaba/dubbo/monitor/support/MonitorFilter.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.support;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.monitor.Monitor;
+import com.alibaba.dubbo.monitor.MonitorFactory;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * MonitorFilter. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})
+public class MonitorFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class);
+
+ private final ConcurrentMap<String, AtomicInteger> concurrents = new ConcurrentHashMap<String, AtomicInteger>();
+
+ private MonitorFactory monitorFactory;
+
+ public void setMonitorFactory(MonitorFactory monitorFactory) {
+ this.monitorFactory = monitorFactory;
+ }
+
+ // 调用过程拦截
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) {
+ RpcContext context = RpcContext.getContext(); // 提供方必须在invoke()之前获取context信息
+ long start = System.currentTimeMillis(); // 记录起始时间戮
+ getConcurrent(invoker, invocation).incrementAndGet(); // 并发计数
+ try {
+ Result result = invoker.invoke(invocation); // 让调用链往下执行
+ collect(invoker, invocation, context, start, false);
+ return result;
+ } catch (RpcException e) {
+ collect(invoker, invocation, context, start, true);
+ throw e;
+ } finally {
+ getConcurrent(invoker, invocation).decrementAndGet(); // 并发计数
+ }
+ } else {
+ return invoker.invoke(invocation);
+ }
+ }
+
+ // 信息采集
+ private void collect(Invoker<?> invoker, Invocation invocation, RpcContext context, long start, boolean error) {
+ try {
+ // ---- 服务信息获取 ----
+ long elapsed = System.currentTimeMillis() - start; // 计算调用耗时
+ int concurrent = getConcurrent(invoker, invocation).get(); // 当前并发数
+ String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
+ String service = invoker.getInterface().getName(); // 获取服务名称
+ String method = invocation.getMethodName(); // 获取方法名
+ URL url = invoker.getUrl().getUrlParameter(Constants.MONITOR_KEY);
+ Monitor monitor = monitorFactory.getMonitor(url);
+ int localPort;
+ String remoteKey;
+ String remoteValue;
+ if (Constants.CONSUMER_SIDE.equals(invoker.getUrl().getParameter(Constants.SIDE_KEY))) {
+ // ---- 服务消费方监控 ----
+ context = RpcContext.getContext(); // 消费方必须在invoke()之后获取context信息
+ localPort = 0;
+ remoteKey = MonitorService.PROVIDER;
+ remoteValue = invoker.getUrl().getAddress();
+ } else {
+ // ---- 服务提供方监控 ----
+ localPort = invoker.getUrl().getPort();
+ remoteKey = MonitorService.CONSUMER;
+ remoteValue = context.getRemoteHost();
+ }
+ monitor.collect(new URL(Constants.COUNT_PROTOCOL,
+ NetUtils.getLocalHost(), localPort,
+ service + "/" + method,
+ MonitorService.APPLICATION, application,
+ MonitorService.INTERFACE, service,
+ MonitorService.METHOD, method,
+ remoteKey, remoteValue,
+ error ? MonitorService.FAILURE : MonitorService.SUCCESS, "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/dubbo-monitor-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-monitor/dubbo-monitor-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..832b9cd
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+monitor=com.alibaba.dubbo.monitor.support.MonitorFilter
\ No newline at end of file
diff --git a/dubbo-monitor/dubbo-monitor-api/src/test/java/com/alibaba/dubbo/monitor/support/MonitorFilterTest.java b/dubbo-monitor/dubbo-monitor-api/src/test/java/com/alibaba/dubbo/monitor/support/MonitorFilterTest.java
new file mode 100644
index 0000000..1d0cb4c
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-api/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.SIDE_KEY + "=" + Constants.CONSUMER_SIDE + "&" + 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 collect(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.getParameter(MonitorService.PROVIDER));
+ Assert.assertEquals(NetUtils.getLocalHost(), lastStatistics.getAddress());
+ Assert.assertEquals(null, lastStatistics.getParameter(MonitorService.CONSUMER));
+ 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-monitor/dubbo-monitor-default/pom.xml b/dubbo-monitor/dubbo-monitor-default/pom.xml
new file mode 100644
index 0000000..1af55af
--- /dev/null
+++ b/dubbo-monitor/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-monitor</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-monitor-default</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</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/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.java b/dubbo-monitor/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.java
new file mode 100644
index 0000000..cb0a445
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.dubbo;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.monitor.Monitor;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * DubboMonitor
+ *
+ * @author william.liangf
+ */
+public class DubboMonitor implements Monitor {
+
+ private static final Logger logger = LoggerFactory.getLogger(DubboMonitor.class);
+
+ private static final int LENGTH = 10;
+
+ // 定时任务执行器
+ private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3, new NamedThreadFactory("DubboMonitorSendTimer", true));
+
+ // 统计信息收集定时器
+ private final ScheduledFuture<?> sendFuture;
+
+ private final Invoker<MonitorService> monitorInvoker;
+
+ private final MonitorService monitorService;
+
+ private final long monitorInterval;
+
+ private final ConcurrentMap<Statistics, AtomicReference<long[]>> statisticsMap = new ConcurrentHashMap<Statistics, AtomicReference<long[]>>();
+
+ public DubboMonitor(Invoker<MonitorService> monitorInvoker, MonitorService monitorService) {
+ this.monitorInvoker = monitorInvoker;
+ this.monitorService = monitorService;
+ this.monitorInterval = monitorInvoker.getUrl().getPositiveParameter("interval", 60000);
+ // 启动统计信息收集定时器
+ sendFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
+ public void run() {
+ // 收集统计信息
+ try {
+ send();
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected error occur at send statistic, cause: " + t.getMessage(), t);
+ }
+ }
+ }, monitorInterval, monitorInterval, TimeUnit.MILLISECONDS);
+ }
+
+ public void send() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Send statistics to monitor " + getUrl());
+ }
+ String timestamp = String.valueOf(System.currentTimeMillis());
+ for (Map.Entry<Statistics, AtomicReference<long[]>> entry : statisticsMap.entrySet()) {
+ // 获取已统计数据
+ Statistics statistics = entry.getKey();
+ AtomicReference<long[]> reference = entry.getValue();
+ long[] numbers = reference.get();
+ long success = numbers[0];
+ long failure = numbers[1];
+ long input = numbers[2];
+ long output = numbers[3];
+ long elapsed = numbers[4];
+ long concurrent = numbers[5];
+ long maxInput = numbers[6];
+ long maxOutput = numbers[7];
+ long maxElapsed = numbers[8];
+ long maxConcurrent = numbers[9];
+
+ // 发送汇总信息
+ URL url = statistics.getUrl()
+ .addParameters(MonitorService.TIMESTAMP, timestamp,
+ MonitorService.SUCCESS, String.valueOf(success),
+ MonitorService.FAILURE, String.valueOf(failure),
+ MonitorService.INPUT, String.valueOf(input),
+ MonitorService.OUTPUT, String.valueOf(output),
+ MonitorService.ELAPSED, String.valueOf(elapsed),
+ MonitorService.CONCURRENT, String.valueOf(concurrent),
+ MonitorService.MAX_INPUT, String.valueOf(maxInput),
+ MonitorService.MAX_OUTPUT, String.valueOf(maxOutput),
+ MonitorService.MAX_ELAPSED, String.valueOf(maxElapsed),
+ MonitorService.MAX_CONCURRENT, String.valueOf(maxConcurrent)
+ );
+ monitorService.collect(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 collect(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/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.java b/dubbo-monitor/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.java
new file mode 100644
index 0000000..023894e
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.dubbo;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.monitor.Monitor;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.monitor.support.AbstractMonitorFactroy;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+/**
+ * DefaultMonitorFactroy
+ *
+ * @author william.liangf
+ */
+public class DubboMonitorFactroy extends AbstractMonitorFactroy {
+
+ private Protocol protocol;
+
+ private ProxyFactory proxyFactory;
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ public void setProxyFactory(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ @Override
+ protected Monitor createMonitor(URL url) {
+ url = url.setProtocol(url.getParameter(Constants.PROTOCOL_KEY, "dubbo"));
+ if (url.getPath() == null || url.getPath().length() == 0) {
+ url = url.setPath(MonitorService.class.getName());
+ }
+ String filter = url.getParameter(Constants.REFERENCE_FILTER_KEY);
+ if (filter == null || filter.length() == 0) {
+ filter = "";
+ } else {
+ filter = filter + ",";
+ }
+ url = url.addParameters(Constants.CLUSTER_KEY, "failsafe", Constants.CHECK_KEY, String.valueOf(false),
+ Constants.REFERENCE_FILTER_KEY, filter + "-monitor");
+ Invoker<MonitorService> monitorInvoker = protocol.refer(MonitorService.class, url);
+ MonitorService monitorService = proxyFactory.getProxy(monitorInvoker);
+ return new DubboMonitor(monitorInvoker, monitorService);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.java b/dubbo-monitor/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.java
new file mode 100644
index 0000000..45008e4
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-default/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.dubbo;
+
+import java.io.Serializable;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.monitor.MonitorService;
+
+/**
+ * Statistics. (SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class Statistics implements Serializable {
+
+ private static final long serialVersionUID = -6921183057683641441L;
+
+ private URL url;
+
+ private String application;
+
+ private String service;
+
+ private String method;
+
+ private String group;
+
+ private String version;
+
+ private String client;
+
+ private String server;
+
+ public Statistics(URL url) {
+ this.url = url;
+ this.application = url.getParameter(MonitorService.APPLICATION);
+ this.service = url.getParameter(MonitorService.INTERFACE);
+ this.method = url.getParameter(MonitorService.METHOD);
+ this.group = url.getParameter(MonitorService.GROUP);
+ this.version = url.getParameter(MonitorService.VERSION);
+ this.client = url.getParameter(MonitorService.CONSUMER, url.getAddress());
+ this.server = url.getParameter(MonitorService.PROVIDER, url.getAddress());
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ public String getApplication() {
+ return application;
+ }
+
+ public Statistics setApplication(String application) {
+ this.application = application;
+ return this;
+ }
+
+ public String getService() {
+ return service;
+ }
+
+ public Statistics setService(String service) {
+ this.service = service;
+ return this;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public Statistics setMethod(String method) {
+ this.method = method;
+ return this;
+ }
+
+ public String getClient() {
+ return client;
+ }
+
+ public Statistics setClient(String client) {
+ this.client = client;
+ return this;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public Statistics setServer(String server) {
+ this.server = server;
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((application == null) ? 0 : application.hashCode());
+ result = prime * result + ((client == null) ? 0 : client.hashCode());
+ result = prime * result + ((group == null) ? 0 : group.hashCode());
+ result = prime * result + ((method == null) ? 0 : method.hashCode());
+ result = prime * result + ((server == null) ? 0 : server.hashCode());
+ result = prime * result + ((service == null) ? 0 : service.hashCode());
+ result = prime * result + ((version == null) ? 0 : version.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Statistics other = (Statistics) obj;
+ if (application == null) {
+ if (other.application != null)
+ return false;
+ } else if (!application.equals(other.application))
+ return false;
+ if (client == null) {
+ if (other.client != null)
+ return false;
+ } else if (!client.equals(other.client))
+ return false;
+ if (group == null) {
+ if (other.group != null)
+ return false;
+ } else if (!group.equals(other.group))
+ return false;
+ if (method == null) {
+ if (other.method != null)
+ return false;
+ } else if (!method.equals(other.method))
+ return false;
+ if (server == null) {
+ if (other.server != null)
+ return false;
+ } else if (!server.equals(other.server))
+ return false;
+ if (service == null) {
+ if (other.service != null)
+ return false;
+ } else if (!service.equals(other.service))
+ return false;
+ if (version == null) {
+ if (other.version != null)
+ return false;
+ } else if (!version.equals(other.version))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return url.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/dubbo-monitor-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory b/dubbo-monitor/dubbo-monitor-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory
new file mode 100644
index 0000000..25ae258
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory
@@ -0,0 +1 @@
+dubbo=com.alibaba.dubbo.monitor.dubbo.DubboMonitorFactroy
\ No newline at end of file
diff --git a/dubbo-monitor/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java b/dubbo-monitor/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java
new file mode 100644
index 0000000..cfb7a87
--- /dev/null
+++ b/dubbo-monitor/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.dubbo;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.monitor.Monitor;
+import com.alibaba.dubbo.monitor.MonitorFactory;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * DubboMonitorTest
+ *
+ * @author william.liangf
+ */
+public class DubboMonitorTest {
+
+ private volatile URL lastStatistics;
+
+ private final Invoker<MonitorService> monitorInvoker = new Invoker<MonitorService>() {
+ public Class<MonitorService> getInterface() {
+ return MonitorService.class;
+ }
+ public URL getUrl() {
+ return URL.valueOf("dubbo://127.0.0.1:7070?interval=20");
+ }
+ public boolean isAvailable() {
+ return false;
+ }
+ public Result invoke(Invocation invocation) throws RpcException {
+ return null;
+ }
+ public void destroy() {
+ }
+ };
+
+ private final MonitorService monitorService = new MonitorService() {
+
+ public void collect(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.collect(statistics);
+ while (lastStatistics == null) {
+ Thread.sleep(10);
+ }
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.APPLICATION), "morgan");
+ Assert.assertEquals(lastStatistics.getProtocol(), "dubbo");
+ Assert.assertEquals(lastStatistics.getHost(), "10.20.153.10");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.APPLICATION), "morgan");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.INTERFACE), "MemberService");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.METHOD), "findPerson");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.CONSUMER), "10.20.153.11");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.SUCCESS), "1");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.FAILURE), "0");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.ELAPSED), "3");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.MAX_ELAPSED), "3");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.CONCURRENT), "1");
+ Assert.assertEquals(lastStatistics.getParameter(MonitorService.MAX_CONCURRENT), "1");
+ monitor.destroy();
+ }
+
+ @Test
+ public void testMonitorFactory() throws Exception {
+ MockMonitorService monitorService = new MockMonitorService();
+ URL statistics = new URL("dubbo", "10.20.153.10", 0)
+ .addParameter(MonitorService.APPLICATION, "morgan")
+ .addParameter(MonitorService.INTERFACE, "MemberService")
+ .addParameter(MonitorService.METHOD, "findPerson")
+ .addParameter(MonitorService.CONSUMER, "10.20.153.11")
+ .addParameter(MonitorService.SUCCESS, 1)
+ .addParameter(MonitorService.FAILURE, 0)
+ .addParameter(MonitorService.ELAPSED, 3)
+ .addParameter(MonitorService.MAX_ELAPSED, 3)
+ .addParameter(MonitorService.CONCURRENT, 1)
+ .addParameter(MonitorService.MAX_CONCURRENT, 1);
+
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ MonitorFactory monitorFactory = ExtensionLoader.getExtensionLoader(MonitorFactory.class).getAdaptiveExtension();
+
+ Exporter<MonitorService> exporter = protocol.export(proxyFactory.getInvoker(monitorService, MonitorService.class, URL.valueOf("dubbo://127.0.0.1:17979/" + MonitorService.class.getName())));
+ try {
+ Monitor monitor = monitorFactory.getMonitor(URL.valueOf("dubbo://127.0.0.1:17979?interval=10"));
+ try {
+ monitor.collect(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/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/MockMonitorService.java b/dubbo-monitor/dubbo-monitor-default/src/test/java/com/alibaba/dubbo/monitor/dubbo/MockMonitorService.java
new file mode 100644
index 0000000..102ea5a
--- /dev/null
+++ b/dubbo-monitor/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 collect(URL statistics) {
+ this.statistics = statistics;
+ }
+
+ public URL getStatistics() {
+ return statistics;
+ }
+
+}
diff --git a/dubbo-monitor/pom.xml b/dubbo-monitor/pom.xml
new file mode 100644
index 0000000..217df02
--- /dev/null
+++ b/dubbo-monitor/pom.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.
+-->
+<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.2.2</version>
+ </parent>
+ <artifactId>dubbo-monitor</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The monitor module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-monitor-api</module>
+ <module>dubbo-monitor-default</module>
+ </modules>
+</project>
diff --git a/dubbo-registry/dubbo-registry-api/pom.xml b/dubbo-registry/dubbo-registry-api/pom.xml
new file mode 100644
index 0000000..d3d1a6c
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/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-registry</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-registry-api</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</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/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/NotifyListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/NotifyListener.java
new file mode 100644
index 0000000..3e633a3
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/NotifyListener.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.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 {
+
+ /**
+ * 当收到服务变更通知时触发。
+ *
+ * 通知需处理契约:<br>
+ * 1. 总是以服务接口和数据类型为维度全量通知,即不会通知一个服务的同类型的部分数据,用户不需要对比上一次通知结果。<br>
+ * 2. 订阅时的第一次通知,必须是一个服务的所有类型数据的全量通知。<br>
+ * 3. 中途变更时,允许不同类型的数据分开通知,比如:providers, consumers, routers, overrides,允许只通知其中一种类型,但该类型的数据必须是全量的,不是增量的。<br>
+ * 4. 如果一种类型的数据为空,需通知一个empty协议并带category参数的标识性URL数据。<br>
+ * 5. 通知者(即注册中心实现)需保证通知的顺序,比如:单线程推送,队列串行化,带版本对比。<br>
+ *
+ * @param urls 已注册信息列表,总不为空,含义同{@link com.alibaba.dubbo.registry.RegistryService#lookup(URL)}的返回值。
+ */
+ void notify(List<URL> urls);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/Registry.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/Registry.java
new file mode 100644
index 0000000..7211b75
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/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/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.java
new file mode 100644
index 0000000..d1703e4
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.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.registry;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * RegistryFactory. (SPI, Singleton, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.registry.support.AbstractRegistryFactory
+ * @author william.liangf
+ */
+@SPI("dubbo")
+public interface RegistryFactory {
+
+ /**
+ * 连接注册中心.
+ *
+ * 连接注册中心需处理契约:<br>
+ * 1. 当设置check=false时表示不检查连接,否则在连接不上时抛出异常。<br>
+ * 2. 支持URL上的username:password权限认证。<br>
+ * 3. 支持backup=10.20.153.10备选注册中心集群地址。<br>
+ * 4. 支持file=registry.cache本地磁盘文件缓存。<br>
+ * 5. 支持timeout=1000请求超时设置。<br>
+ * 6. 支持session=60000会话超时或过期设置。<br>
+ *
+ * @param url 注册中心地址,不允许为空
+ * @return 注册中心引用,总不返回空
+ */
+ @Adaptive({"protocol"})
+ Registry getRegistry(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/RegistryService.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/RegistryService.java
new file mode 100644
index 0000000..4f1a67e
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/RegistryService.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.registry;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * RegistryService. (SPI, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.registry.Registry
+ * @see com.alibaba.dubbo.registry.RegistryFactory#getRegistry(URL)
+ * @author william.liangf
+ */
+public interface RegistryService {
+
+ /**
+ * 注册数据,比如:提供者地址,消费者地址,路由规则,覆盖规则,等数据。
+ *
+ * 注册需处理契约:<br>
+ * 1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试,否则抛出异常。<br>
+ * 2. 当URL设置了dynamic=false参数,则需持久存储,否则,当注册者出现断电等情况异常退出时,需自动删除。<br>
+ * 3. 当URL设置了category=routers时,表示分类存储,缺省类别为providers,可按分类部分通知数据。<br>
+ * 4. 当注册中心重启,网络抖动,不能丢失数据,包括断线自动删除数据。<br>
+ * 5. 允许URI相同但参数不同的URL并存,不能覆盖。<br>
+ *
+ * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
+ */
+ void register(URL url);
+
+ /**
+ * 取消注册.
+ *
+ * 取消注册需处理契约:<br>
+ * 1. 如果是dynamic=false的持久存储数据,找不到注册数据,则抛IllegalStateException,否则忽略。<br>
+ * 2. 按全URL匹配取消注册。<br>
+ *
+ * @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设置了category=routers,只通知指定分类的数据,多个分类用逗号分隔,并允许星号通配,表示订阅所有分类数据。<br>
+ * 3. 允许以interface,group,version,classifier作为条件查询,如:interface=com.alibaba.foo.BarService&version=1.0.0<br>
+ * 4. 并且查询条件允许星号通配,订阅所有接口的所有分组的所有版本,或:interface=*&group=*&version=*&classifier=*<br>
+ * 5. 当注册中心重启,网络抖动,需自动恢复订阅请求。<br>
+ * 6. 允许URI相同但参数不同的URL并存,不能覆盖。<br>
+ * 7. 必须阻塞订阅过程,等第一次通知完后再返回。<br>
+ *
+ * @param url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
+ * @param listener 变更事件监听器,不允许为空
+ */
+ void subscribe(URL url, NotifyListener listener);
+
+ /**
+ * 取消订阅.
+ *
+ * 取消订阅需处理契约:<br>
+ * 1. 如果没有订阅,直接忽略。<br>
+ * 2. 按全URL匹配取消订阅。<br>
+ *
+ * @param url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
+ * @param listener 变更事件监听器,不允许为空
+ */
+ void unsubscribe(URL url, NotifyListener listener);
+
+ /**
+ * 查询符合条件的已注册数据,与订阅的推模式相对应,这里为拉模式,只返回一次结果。
+ *
+ * @see com.alibaba.dubbo.registry.NotifyListener#notify(List)
+ * @param url 查询条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
+ * @return 已注册信息列表,可能为空,含义同{@link com.alibaba.dubbo.registry.NotifyListener#notify(List<URL>)}的参数。
+ */
+ List<URL> lookup(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/integration/RegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/integration/RegistryDirectory.java
new file mode 100644
index 0000000..3a8cb7e
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/integration/RegistryDirectory.java
@@ -0,0 +1,686 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integration;
+
+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 com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Configurator;
+import com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+import com.alibaba.dubbo.rpc.cluster.directory.AbstractDirectory;
+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
+import com.alibaba.dubbo.rpc.cluster.support.ClusterUtils;
+import com.alibaba.dubbo.rpc.protocol.InvokerWrapper;
+
+/**
+ * RegistryDirectory
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(RegistryDirectory.class);
+
+ private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
+
+ private static final RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
+
+ private static final ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getAdaptiveExtension();
+
+ private Protocol protocol; // 注入时初始化,断言不为null
+
+ private Registry registry; // 注入时初始化,断言不为null
+
+ private final String serviceKey; // 构造时初始化,断言不为null
+
+ private final Class<T> serviceType; // 构造时初始化,断言不为null
+
+ private final Map<String, String> queryMap; // 构造时初始化,断言不为null
+
+ private final URL directoryUrl; // 构造时初始化,断言不为null,并且总是赋非null值
+
+ private final String[] serviceMethods;
+
+ private final boolean multiGroup;
+
+ private URL subscribeUrl;
+
+ private volatile boolean forbidden = false;
+
+ private volatile URL overrideDirectoryUrl; // 构造时初始化,断言不为null,并且总是赋非null值
+
+ /*override规则
+ * 优先级:override>-D>consumer>provider
+ * 第一种规则:针对某个provider <ip:port,timeout=100>
+ * 第二种规则:针对所有provider <* ,timeout=5000>
+ */
+ private volatile List<Configurator> configurators; // 初始为null以及中途可能被赋为null,请使用局部变量引用
+
+ // Map<url, Invoker> cache service url to invoker mapping.
+ private volatile Map<String, Invoker<T>> urlInvokerMap; // 初始为null以及中途可能被赋为null,请使用局部变量引用
+
+ // Map<methodName, Invoker> cache service method to invokers mapping.
+ private volatile Map<String, List<Invoker<T>>> methodInvokerMap; // 初始为null以及中途可能被赋为null,请使用局部变量引用
+
+ // Set<invokerUrls> cache invokeUrls to invokers mapping.
+ private volatile Set<URL> cachedInvokerUrls; // 初始为null以及中途可能被赋为null,请使用局部变量引用
+
+ public RegistryDirectory(Class<T> serviceType, URL url) {
+ super(url);
+ if(serviceType == null )
+ throw new IllegalArgumentException("service type is null.");
+ if(url.getServiceKey() == null || url.getServiceKey().length() == 0)
+ throw new IllegalArgumentException("registry serviceKey is null.");
+ this.serviceType = serviceType;
+ this.serviceKey = url.getServiceKey();
+ this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
+ this.overrideDirectoryUrl = this.directoryUrl = url.clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY);
+ String group = directoryUrl.getParameter( Constants.GROUP_KEY, "" );
+ this.multiGroup = group != null && ("*".equals(group) || group.contains( "," ));
+ String methods = queryMap.get(Constants.METHODS_KEY);
+ this.serviceMethods = methods == null ? null : Constants.COMMA_SPLIT_PATTERN.split(methods);
+ }
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ public void setRegistry(Registry registry) {
+ this.registry = registry;
+ }
+
+ public void subscribe(URL url) {
+ this.subscribeUrl = url;
+ registry.subscribe(url, this);
+ }
+
+ public void destroy() {
+ if(isDestroyed()) {
+ return;
+ }
+ // unsubscribe.
+ try {
+ if(subscribeUrl != null && registry != null && registry.isAvailable()) {
+ registry.unsubscribe(subscribeUrl, 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) {
+ List<URL> invokerUrls = new ArrayList<URL>();
+ List<URL> routerUrls = new ArrayList<URL>();
+ List<URL> configuratorUrls = new ArrayList<URL>();
+ for (URL url : urls) {
+ String protocol = url.getProtocol();
+ String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
+ if (Constants.ROUTERS_CATEGORY.equals(category)
+ || Constants.ROUTE_PROTOCOL.equals(protocol)) {
+ routerUrls.add(url);
+ } else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
+ || Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
+ configuratorUrls.add(url);
+ } else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
+ invokerUrls.add(url);
+ } else {
+ logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
+ }
+ }
+ // configurators
+ if (configuratorUrls != null && configuratorUrls.size() >0 ){
+ this.configurators = toConfigurators(configuratorUrls);
+ }
+ // routers
+ if (routerUrls != null && routerUrls.size() >0 ){
+ List<Router> routers = toRouters(routerUrls);
+ if(routers != null){ // null - do nothing
+ setRouters(routers);
+ }
+ }
+ List<Configurator> localConfigurators = this.configurators; // local reference
+ // 合并override参数
+ this.overrideDirectoryUrl = directoryUrl;
+ if (localConfigurators != null && localConfigurators.size() > 0) {
+ for (Configurator configurator : localConfigurators) {
+ this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
+ }
+ }
+ // providers
+ refreshInvoker(invokerUrls);
+ }
+
+
+ /**
+ * 根据invokerURL列表转换为invoker列表。转换规则如下:
+ * 1.如果url已经被转换为invoker,则不在重新引用,直接从缓存中获取,注意如果url中任何一个参数变更也会重新引用
+ * 2.如果传入的invoker列表不为空,则表示最新的invoker列表
+ * 3.如果传入的invokerUrl列表是空,则表示只是下发的override规则或route规则,需要重新交叉对比,决定是否需要重新引用。
+ * @param invokerUrls 传入的参数不能为null
+ */
+ private void refreshInvoker(List<URL> invokerUrls){
+ if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
+ && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
+ this.forbidden = true; // 禁止访问
+ this.methodInvokerMap = null; // 置空列表
+ destroyAllInvokers(); // 关闭所有Invoker
+ } else {
+ this.forbidden = false; // 允许访问
+ Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
+ if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
+ invokerUrls.addAll(this.cachedInvokerUrls);
+ } else {
+ this.cachedInvokerUrls = new HashSet<URL>();
+ this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
+ }
+ if (invokerUrls.size() ==0 ){
+ return;
+ }
+ Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表
+ Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
+ // state change
+ //如果计算错误,则不进行处理.
+ if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
+ logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
+ return ;
+ }
+ this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
+ this.urlInvokerMap = newUrlInvokerMap;
+ try{
+ destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
+ }catch (Exception e) {
+ logger.warn("destroyUnusedInvokers error. ", e);
+ }
+ }
+ }
+
+ private Map<String, List<Invoker<T>>> toMergeMethodInvokerMap(Map<String, List<Invoker<T>>> methodMap) {
+ Map<String, List<Invoker<T>>> result = new HashMap<String, List<Invoker<T>>>();
+ for (Map.Entry<String, List<Invoker<T>>> entry : methodMap.entrySet()) {
+ String method = entry.getKey();
+ List<Invoker<T>> invokers = entry.getValue();
+ Map<String, List<Invoker<T>>> groupMap = new HashMap<String, List<Invoker<T>>>();
+ for (Invoker<T> invoker : invokers) {
+ String group = invoker.getUrl().getParameter(Constants.GROUP_KEY, "");
+ List<Invoker<T>> groupInvokers = groupMap.get(group);
+ if (groupInvokers == null) {
+ groupInvokers = new ArrayList<Invoker<T>>();
+ groupMap.put(group, groupInvokers);
+ }
+ groupInvokers.add(invoker);
+ }
+ if (groupMap.size() == 1) {
+ result.put(method, groupMap.values().iterator().next());
+ } else if (groupMap.size() > 1) {
+ List<Invoker<T>> groupInvokers = new ArrayList<Invoker<T>>();
+ for (List<Invoker<T>> groupList : groupMap.values()) {
+ groupInvokers.add(cluster.join(new StaticDirectory<T>(groupList)));
+ }
+ result.put(method, groupInvokers);
+ } else {
+ result.put(method, invokers);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * 将overrideURL转换为map,供重新refer时使用.
+ * 每次下发全部规则,全部重新组装计算
+ * @param urls
+ * 契约:
+ * </br>1.override://0.0.0.0/...(或override://ip:port...?anyhost=true)¶1=value1...表示全局规则(对所有的提供者全部生效)
+ * </br>2.override://ip:port...?anyhost=false 特例规则(只针对某个提供者生效)
+ * </br>3.不支持override://规则... 需要注册中心自行计算.
+ * </br>4.不带参数的override://0.0.0.0/ 表示清除override
+ * @return
+ */
+ private List<Configurator> toConfigurators(List<URL> urls){
+ List<Configurator> configurators = new ArrayList<Configurator>(urls.size());
+ if (urls == null || urls.size() == 0){
+ return configurators;
+ }
+ for(URL url : urls){
+ if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
+ configurators.clear();
+ break;
+ }
+ Map<String,String> override = new HashMap<String, String>(url.getParameters());
+ //override 上的anyhost可能是自动添加的,不能影响改变url判断
+ override.remove(Constants.ANYHOST_KEY);
+ if (override.size() == 0){
+ configurators.clear();
+ continue;
+ }
+ configurators.add(configuratorFactory.getConfigurator(url));
+ }
+ Collections.sort(configurators);
+ return configurators;
+ }
+
+ /**
+ *
+ * @param urls
+ * @return null : no routers ,do nothing
+ * else :routers list
+ */
+ private List<Router> toRouters(List<URL> urls) {
+ List<Router> routers = new ArrayList<Router>();
+ if(urls == null || urls.size() < 1){
+ return routers ;
+ }
+ if (urls != null && urls.size() > 0) {
+ for (URL url : urls) {
+ if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
+ continue;
+ }
+ String routerType = url.getParameter(Constants.ROUTER_KEY);
+ if (routerType != null && routerType.length() > 0){
+ url = url.setProtocol(routerType);
+ }
+ try{
+ Router router = routerFactory.getRouter(url);
+ if (!routers.contains(router))
+ routers.add(router);
+ } catch (Throwable t) {
+ logger.error("convert router url to router error, url: "+ url, t);
+ }
+ }
+ }
+ return routers;
+ }
+
+ /**
+ * 将urls转成invokers,如果url已经被refer过,不再重新引用。
+ *
+ * @param urls
+ * @param overrides
+ * @param query
+ * @return invokers
+ */
+ private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
+ Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
+ if(urls == null || urls.size() == 0){
+ return newUrlInvokerMap;
+ }
+ Set<String> keys = new HashSet<String>();
+ String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
+ for (URL providerUrl : urls) {
+ //如果reference端配置了protocol,则只选择匹配的protocol
+ if (queryProtocols != null && queryProtocols.length() >0) {
+ boolean accept = false;
+ String[] acceptProtocols = queryProtocols.split(",");
+ for (String acceptProtocol : acceptProtocols) {
+ if (providerUrl.getProtocol().equals(acceptProtocol)) {
+ accept = true;
+ break;
+ }
+ }
+ if (!accept) {
+ continue;
+ }
+ }
+ if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
+ continue;
+ }
+ if (! ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
+ logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()
+ + ", supported protocol: "+ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
+ continue;
+ }
+ URL url = mergeUrl(providerUrl);
+
+ String key = url.toFullString(); // URL参数是排序的
+ if (keys.contains(key)) { // 重复URL
+ continue;
+ }
+ keys.add(key);
+ // 缓存key为没有合并消费端参数的URL,不管消费端如何合并参数,如果服务端URL发生变化,则重新refer
+ Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
+ Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
+ if (invoker == null) { // 缓存中没有,重新refer
+ try {
+ boolean enabled = true;
+ if (url.hasParameter(Constants.DISABLED_KEY)) {
+ enabled = ! url.getParameter(Constants.DISABLED_KEY, false);
+ } else {
+ enabled = url.getParameter(Constants.ENABLED_KEY, true);
+ }
+ if (enabled) {
+ invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
+ }
+ } catch (Throwable t) {
+ logger.error("Failed to refer invoker for interface:"+serviceType+",url:("+url+")" + t.getMessage(), t);
+ }
+ if (invoker != null) { // 将新的引用放入缓存
+ newUrlInvokerMap.put(key, invoker);
+ }
+ }else {
+ newUrlInvokerMap.put(key, invoker);
+ }
+ }
+ keys.clear();
+ return newUrlInvokerMap;
+ }
+
+ /**
+ * 合并url参数 顺序为override > -D >Consumer > Provider
+ * @param providerUrl
+ * @param overrides
+ * @return
+ */
+ private URL mergeUrl(URL providerUrl){
+ providerUrl = ClusterUtils.mergeUrl(providerUrl, queryMap); // 合并消费端参数
+
+ List<Configurator> localConfigurators = this.configurators; // local reference
+ if (localConfigurators != null && localConfigurators.size() > 0) {
+ for (Configurator configurator : localConfigurators) {
+ providerUrl = configurator.configure(providerUrl);
+ }
+ }
+
+ providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // 不检查连接是否成功,总是创建Invoker!
+
+ //directoryUrl 与 override 合并是在notify的最后,这里不能够处理
+ this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // 合并提供者参数
+
+ if ((providerUrl.getPath() == null || providerUrl.getPath().length() == 0)
+ && "dubbo".equals(providerUrl.getProtocol())) { // 兼容1.0
+ //fix by tony.chenl DUBBO-44
+ String path = directoryUrl.getParameter(Constants.INTERFACE_KEY);
+ if (path != null) {
+ int i = path.indexOf('/');
+ if (i >= 0) {
+ path = path.substring(i + 1);
+ }
+ i = path.lastIndexOf(':');
+ if (i >= 0) {
+ path = path.substring(0, i);
+ }
+ providerUrl = providerUrl.setPath(path);
+ }
+ }
+ return providerUrl;
+ }
+
+ private List<Invoker<T>> route(List<Invoker<T>> invokers, String method) {
+ Invocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);
+ List<Router> routers = getRouters();
+ if (routers != null) {
+ for (Router router : routers) {
+ if (router.getUrl() != null && ! router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) {
+ invokers = router.route(invokers, getUrl(), invocation);
+ }
+ }
+ }
+ return invokers;
+ }
+
+ /**
+ * 将invokers列表转成与方法的映射关系
+ *
+ * @param invokersMap Invoker列表
+ * @return Invoker与方法的映射关系
+ */
+ private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) {
+ Map<String, List<Invoker<T>>> newMethodInvokerMap = new HashMap<String, List<Invoker<T>>>();
+ // 按提供者URL所声明的methods分类,兼容注册中心执行路由过滤掉的methods
+ List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();
+ if (invokersMap != null && invokersMap.size() > 0) {
+ 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 = newMethodInvokerMap.get(method);
+ if (methodInvokers == null) {
+ methodInvokers = new ArrayList<Invoker<T>>();
+ newMethodInvokerMap.put(method, methodInvokers);
+ }
+ methodInvokers.add(invoker);
+ }
+ }
+ }
+ }
+ invokersList.add(invoker);
+ }
+ }
+ newMethodInvokerMap.put(Constants.ANY_VALUE, invokersList);
+ if (serviceMethods != null && serviceMethods.length > 0) {
+ for (String method : serviceMethods) {
+ List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
+ if (methodInvokers == null || methodInvokers.size() == 0) {
+ methodInvokers = invokersList;
+ }
+ newMethodInvokerMap.put(method, route(methodInvokers, method));
+ }
+ }
+ // sort and unmodifiable
+ for (String method : new HashSet<String>(newMethodInvokerMap.keySet())) {
+ List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
+ Collections.sort(methodInvokers, InvokerComparator.getComparator());
+ newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));
+ }
+ return Collections.unmodifiableMap(newMethodInvokerMap);
+ }
+
+ /**
+ * 关闭所有Invoker
+ */
+ private void destroyAllInvokers() {
+ Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
+ if(localUrlInvokerMap != null) {
+ for (Invoker<T> invoker : new ArrayList<Invoker<T>>(localUrlInvokerMap.values())) {
+ try {
+ invoker.destroy();
+ } catch (Throwable t) {
+ logger.warn("Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t);
+ }
+ }
+ localUrlInvokerMap.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;
+ Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // local reference
+ if (localMethodInvokerMap != null && localMethodInvokerMap.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 = localMethodInvokerMap.get(methodName + "." + args[0]); // 可根据第一个参数枚举路由
+ }
+ if(invokers == null) {
+ invokers = localMethodInvokerMap.get(methodName);
+ }
+ if(invokers == null) {
+ invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
+ }
+ if(invokers == null) {
+ Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator();
+ if (iterator.hasNext()) {
+ invokers = iterator.next();
+ }
+ }
+ }
+ return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
+ }
+
+ public Class<T> getInterface() {
+ return serviceType;
+ }
+
+ public URL getUrl() {
+ return this.overrideDirectoryUrl;
+ }
+
+ public boolean isAvailable() {
+ if (isDestroyed()) {
+ return false;
+ }
+ Map<String, Invoker<T>> localUrlInvokerMap = urlInvokerMap;
+ if (localUrlInvokerMap != null && localUrlInvokerMap.size() > 0) {
+ for (Invoker<T> invoker : new ArrayList<Invoker<T>>(localUrlInvokerMap.values())) {
+ if (invoker.isAvailable()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Haomin: added for test purpose
+ */
+ public Map<String, Invoker<T>> getUrlInvokerMap(){
+ return urlInvokerMap;
+ }
+
+ /**
+ * Haomin: added for test purpose
+ */
+ public Map<String, List<Invoker<T>>> getMethodInvokerMap(){
+ return methodInvokerMap;
+ }
+
+ private static class InvokerComparator implements Comparator<Invoker<?>> {
+
+ private static final InvokerComparator comparator = new InvokerComparator();
+
+ public static InvokerComparator getComparator() {
+ return comparator;
+ }
+
+ private InvokerComparator() {}
+
+ public int compare(Invoker<?> o1, Invoker<?> o2) {
+ return o1.getUrl().toString().compareTo(o2.getUrl().toString());
+ }
+
+ }
+
+ /**
+ * 代理类,主要用于存储注册中心下发的url地址,用于重新重新refer时能够根据providerURL queryMap overrideMap重新组装
+ *
+ * @author chao.liuc
+ *
+ * @param <T>
+ */
+ private static class InvokerDelegete<T> extends InvokerWrapper<T>{
+ private URL providerUrl;
+ public InvokerDelegete(Invoker<T> invoker, URL url, URL providerUrl) {
+ super(invoker, url);
+ this.providerUrl = providerUrl;
+ }
+ public URL getProviderUrl() {
+ return providerUrl;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/integration/RegistryProtocol.java
new file mode 100644
index 0000000..0d4dfb9
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/integration/RegistryProtocol.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integration;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.protocol.InvokerWrapper;
+
+/**
+ * RegistryProtocol
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class RegistryProtocol implements Protocol {
+
+ private Cluster cluster;
+
+ public void setCluster(Cluster cluster) {
+ this.cluster = cluster;
+ }
+
+ private Protocol protocol;
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ private RegistryFactory registryFactory;
+
+ public void setRegistryFactory(RegistryFactory registryFactory) {
+ this.registryFactory = registryFactory;
+ }
+
+ public int getDefaultPort() {
+ return 9090;
+ }
+
+ private static RegistryProtocol INSTANCE;
+
+ public RegistryProtocol() {
+ INSTANCE = this;
+ }
+
+ public static RegistryProtocol getRegistryProtocol() {
+ if (INSTANCE == null) {
+ ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(Constants.REGISTRY_PROTOCOL); // load
+ }
+ return INSTANCE;
+ }
+
+ //用于解决rmi重复暴露端口冲突的问题,已经暴露过的服务不再重新暴露
+ //providerurl <--> exporter
+ private final Map<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<String, ExporterChangeableWrapper<?>>();
+
+ private final NotifyListener listener = new OverrideListener();
+
+ private final static Logger logger = LoggerFactory.getLogger(RegistryProtocol.class);
+
+ public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
+ //export invoker
+ final ExporterChangeableWrapper<T> exporter = doLocolExport(originInvoker);
+ //registry provider
+ Registry registry = doRegister(originInvoker);
+ //设置exporter与registry的关系 (for unexport)
+ exporter.addRegistry(registry);
+ //保证每次export都返回一个新的exporter实例
+ return new Exporter<T>() {
+ public Invoker<T> getInvoker() {
+ return exporter.getInvoker();
+ }
+ public void unexport() {
+ exporter.unexport();
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> ExporterChangeableWrapper<T> doLocolExport(final Invoker<T> originInvoker){
+ String key = getCacheKey(originInvoker);
+ ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
+ if (exporter == null) {
+ synchronized (bounds) {
+ exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
+ if (exporter == null) {
+ final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
+ exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
+ bounds.put(key, exporter);
+ }
+ }
+ }
+ return (ExporterChangeableWrapper<T>) exporter;
+ }
+
+ /**
+ * 对修改了url的invoker重新export
+ * @param originInvoker
+ * @param newInvokerUrl
+ */
+ @SuppressWarnings("unchecked")
+ private <T> void doChangeLocolExport(final Invoker<T> originInvoker, URL newInvokerUrl){
+ String key = getCacheKey(originInvoker);
+ final ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
+ if (exporter == null){
+ logger.warn(new IllegalStateException("error state, exporter should not be null"));
+ return ;//不存在是异常场景 直接返回
+ } else {
+ final Invoker<T> invokerDelegete = new InvokerDelegete<T>(originInvoker, newInvokerUrl);
+ exporter.setExporter(protocol.export(invokerDelegete));
+ }
+ }
+
+ /**
+ * 注册 provider
+ * @param originInvoker
+ * @return
+ */
+ private Registry doRegister(final Invoker<?> originInvoker){
+ final Registry registry = getRegistry(originInvoker);
+ final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
+ registry.register(registedProviderUrl);
+ // 订阅override数据
+ // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
+ registry.subscribe(registedProviderUrl.setProtocol(Constants.PROVIDER_PROTOCOL)
+ .addParameters(Constants.CATEGORY_KEY, Constants.CONFIGURATORS_CATEGORY,
+ Constants.CHECK_KEY, String.valueOf(false)), listener);
+ return registry;
+ }
+
+ /**
+ * 根据invoker的地址获取registry实例
+ * @param originInvoker
+ * @return
+ */
+ private Registry getRegistry(final Invoker<?> originInvoker){
+ URL registryUrl = originInvoker.getUrl();
+ if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
+ String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
+ registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
+ }
+ return registryFactory.getRegistry(registryUrl);
+ }
+
+ /**
+ * 返回注册到注册中心的URL,对URL参数进行一次过滤
+ * @param originInvoker
+ * @return
+ */
+ private URL getRegistedProviderUrl(final Invoker<?> originInvoker){
+ URL providerUrl = getProviderUrl(originInvoker);
+ //注册中心看到的地址
+ final URL registedProviderUrl = providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameter(Constants.MONITOR_KEY);
+ return registedProviderUrl;
+ }
+
+ /**
+ * 通过invoker的url 获取 providerUrl的地址
+ * @param origininvoker
+ * @return
+ */
+ private URL getProviderUrl(final Invoker<?> origininvoker){
+ String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY);
+ if (export == null || export.length() == 0) {
+ throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl());
+ }
+
+ URL providerUrl = URL.valueOf(export);
+ return providerUrl;
+ }
+
+ /**
+ * 获取invoker在bounds中缓存的key
+ * @param originInvoker
+ * @return
+ */
+ private String getCacheKey(final Invoker<?> originInvoker){
+ URL providerUrl = getProviderUrl(originInvoker);
+ String key = providerUrl.removeParameters("dynamic", "enabled").toFullString();
+ return key;
+ }
+
+ public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+ url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
+ Registry registry = registryFactory.getRegistry(url);
+
+ // group="a,b" or group="*"
+ Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
+ String group = qs.get(Constants.GROUP_KEY);
+ if (group != null && group.length() > 0 ) {
+ if ( ( Constants.COMMA_SPLIT_PATTERN.split( group ) ).length > 1
+ || "*".equals( group ) ) {
+ return doRefer( getMergeableCluster(), registry, type, url );
+ }
+ }
+ return doRefer(cluster, registry, type, url);
+ }
+
+ private Cluster getMergeableCluster() {
+ return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("mergeable");
+ }
+
+ private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
+ RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
+ directory.setRegistry(registry);
+ directory.setProtocol(protocol);
+ URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
+ if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
+ && url.getParameter(Constants.REGISTER_KEY, true)) {
+ registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
+ Constants.CHECK_KEY, String.valueOf(false)));
+ }
+ directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
+ Constants.PROVIDERS_CATEGORY
+ + "," + Constants.CONFIGURATORS_CATEGORY
+ + "," + Constants.ROUTERS_CATEGORY));
+ return cluster.join(directory);
+ }
+
+ //过滤URL中不需要输出的参数(以点号开头的)
+ private static String[] getFilteredKeys(URL url) {
+ Map<String, String> params = url.getParameters();
+ if (params != null && !params.isEmpty()) {
+ List<String> filteredKeys = new ArrayList<String>();
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ if (entry != null && entry.getKey() != null && entry.getKey().startsWith(Constants.HIDE_KEY_PREFIX)) {
+ filteredKeys.add(entry.getKey());
+ }
+ }
+ return filteredKeys.toArray(new String[filteredKeys.size()]);
+ } else {
+ return new String[] {};
+ }
+ }
+
+ public void destroy() {
+ List<Exporter<?>> exporters = new ArrayList<Exporter<?>>(bounds.values());
+ for(Exporter<?> exporter :exporters){
+ exporter.unexport();
+ }
+ bounds.clear();
+ }
+
+
+ /*重新export 1.protocol中的exporter destory问题
+ *1.要求registryprotocol返回的exporter可以正常destroy
+ *2.notify后不需要重新向注册中心注册
+ *3.export 方法传入的invoker最好能一直作为exporter的invoker.
+ */
+ private class OverrideListener implements NotifyListener {
+
+ /*
+ * provider 端可识别的override url只有这两种.
+ * override://0.0.0.0/serviceName?timeout=10
+ * override://0.0.0.0/?timeout=10
+ */
+ public void notify(List<URL> urls) {
+ List<ExporterChangeableWrapper<?>> exporters = new ArrayList<ExporterChangeableWrapper<?>>(bounds.values());
+ for (ExporterChangeableWrapper<?> exporter : exporters){
+ Invoker<?> invoker = exporter.getOriginInvoker();
+ final Invoker<?> originInvoker ;
+ if (invoker instanceof InvokerDelegete){
+ originInvoker = ((InvokerDelegete<?>)invoker).getInvoker();
+ }else {
+ originInvoker = invoker;
+ }
+
+ URL originUrl = RegistryProtocol.this.getProviderUrl(originInvoker);
+ URL newUrl = getNewInvokerUrl(originUrl, urls);
+
+ if (! originUrl.toFullString().equals(newUrl.toFullString())){
+ RegistryProtocol.this.doChangeLocolExport(originInvoker, newUrl);
+ }
+ }
+ }
+
+ private URL getNewInvokerUrl(final URL originUrl, final List<URL> urls){
+ URL newUrl = originUrl;
+ //override://0.0.0.0/?timeout=10 ip:port无意义
+ for (URL overrideUrl : urls){
+ if (overrideUrl.getServiceInterface() == null){
+ newUrl = newUrl.addParameters(overrideUrl.getParameters());
+ }
+ }
+ //override://0.0.0.0/serviceName?timeout=10
+ for (URL overrideUrl : urls){
+ if (originUrl.getServiceKey().equals(overrideUrl.getServiceKey())){
+ newUrl = newUrl.addParameters(overrideUrl.getParameters());
+ }
+ }
+
+ return newUrl;
+ }
+ }
+
+ public static class InvokerDelegete<T> extends InvokerWrapper<T>{
+ private final Invoker<T> invoker;
+ /**
+ * @param invoker
+ * @param url invoker.getUrl返回此值
+ */
+ public InvokerDelegete(Invoker<T> invoker, URL url){
+ super(invoker, url);
+ this.invoker = invoker;
+ }
+ public Invoker<T> getInvoker(){
+ if (invoker instanceof InvokerDelegete){
+ return ((InvokerDelegete<T>)invoker).getInvoker();
+ } else {
+ return invoker;
+ }
+ }
+ }
+
+ /**
+ * exporter代理,建立返回的exporter与protocol export出的exporter的对应关系,在override时可以进行关系修改.
+ *
+ * @author chao.liuc
+ *
+ * @param <T>
+ */
+ private class ExporterChangeableWrapper<T> implements Exporter<T>{
+ private Exporter<T> exporter;
+ private final Invoker<T> originInvoker;
+ private final List<Registry> registrys = new ArrayList<Registry>();
+
+ public ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker){
+ this.exporter = exporter;
+ this.originInvoker = originInvoker;
+ }
+
+ public Invoker<T> getOriginInvoker() {
+ return originInvoker;
+ }
+
+ public Invoker<T> getInvoker() {
+ return exporter.getInvoker();
+ }
+
+ public void setExporter(Exporter<T> exporter){
+ this.exporter = exporter;
+ }
+
+ public void addRegistry(final Registry registry) {
+ if (registry != null && ! registrys.contains(registry)){
+ registrys.add(registry);
+ }
+ }
+
+ public void unexport() {
+ String key = getCacheKey(this.originInvoker);
+ bounds.remove(key);
+ try {
+ for (Registry registry: registrys) {
+ if (registry != null && registry.isAvailable()) {
+ registry.unregister(getRegistedProviderUrl(originInvoker));
+ }
+ }
+ } finally {
+ exporter.unexport();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/pages/RegisteredPageHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/pages/RegisteredPageHandler.java
new file mode 100644
index 0000000..3b2b496
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/pages/RegisteredPageHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.pages;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * RegisteredPageHandler
+ *
+ * @author william.liangf
+ */
+public class RegisteredPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String registryAddress = url.getParameter("registry", "");
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Collection<Registry> registries = AbstractRegistryFactory.getRegistries();
+ StringBuilder select = new StringBuilder();
+ Registry registry = null;
+ if (registries != null && registries.size() > 0) {
+ if (registries.size() == 1) {
+ registry = registries.iterator().next();
+ select.append(" > " + registry.getUrl().getAddress());
+ } else {
+ select.append(" > <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<URL> services = ((AbstractRegistry) registry).getRegistered();
+ if (services != null && services.size() > 0) {
+ for (URL u : services) {
+ List<String> row = new ArrayList<String>();
+ row.add(u.toFullString().replace("<", "<").replace(">", ">"));
+ rows.add(row);
+ }
+ }
+ }
+ return new Page("<a href=\"registries.html\">Registries</a>" + select.toString() + " > 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/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/pages/RegistriesPageHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/pages/RegistriesPageHandler.java
new file mode 100644
index 0000000..bfe06ff
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/pages/RegistriesPageHandler.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.pages;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.page.Menu;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * RegistriesPageHandler
+ *
+ * @author william.liangf
+ */
+@Menu(name = "Registries", desc = "Show connected registries.", order = 10000)
+public class RegistriesPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Collection<Registry> registries = AbstractRegistryFactory.getRegistries();
+ int registeredCount = 0;
+ int subscribedCount = 0;
+ if (registries != null && registries.size() > 0) {
+ for (Registry registry : registries) {
+ String server = registry.getUrl().getAddress();
+ List<String> row = new ArrayList<String>();
+ row.add(NetUtils.getHostName(server) + "/" + server);
+ if (registry.isAvailable()) {
+ row.add("<font color=\"green\">Connected</font>");
+ } else {
+ row.add("<font color=\"red\">Disconnected</font>");
+ }
+ int registeredSize = 0;
+ int subscribedSize = 0;
+ if (registry instanceof AbstractRegistry) {
+ registeredSize = ((AbstractRegistry) registry).getRegistered().size();
+ registeredCount += registeredSize;
+ subscribedSize = ((AbstractRegistry) registry).getSubscribed().size();
+ subscribedCount += subscribedSize;
+ }
+ row.add("<a href=\"registered.html?registry=" + server + "\">Registered(" + registeredSize + ")</a>");
+ row.add("<a href=\"subscribed.html?registry=" + server + "\">Subscribed(" + subscribedSize + ")</a>");
+ rows.add(row);
+ }
+ }
+ return new Page("Registries", "Registries (" + rows.size() + ")",
+ new String[] { "Registry Address:", "Status", "Registered(" + registeredCount + ")", "Subscribed(" + subscribedCount + ")" }, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/pages/SubscribedPageHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/pages/SubscribedPageHandler.java
new file mode 100644
index 0000000..c1e42e9
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/pages/SubscribedPageHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.pages;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * SubscribedPageHandler
+ *
+ * @author william.liangf
+ */
+public class SubscribedPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String registryAddress = url.getParameter("registry", "");
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Collection<Registry> registries = AbstractRegistryFactory.getRegistries();
+ StringBuilder select = new StringBuilder();
+ Registry registry = null;
+ if (registries != null && registries.size() > 0) {
+ if (registries.size() == 1) {
+ registry = registries.iterator().next();
+ select.append(" > " + registry.getUrl().getAddress());
+ } else {
+ select.append(" > <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<URL> services = ((AbstractRegistry) registry).getSubscribed().keySet();
+ if (services != null && services.size() > 0) {
+ for (URL u : services) {
+ List<String> row = new ArrayList<String>();
+ row.add(u.toFullString().replace("<", "<").replace(">", ">"));
+ rows.add(row);
+ }
+ }
+ }
+ return new Page("<a href=\"registries.html\">Registries</a>" + select.toString() + " > <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/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/status/RegistryStatusChecker.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/status/RegistryStatusChecker.java
new file mode 100644
index 0000000..ca6e4a5
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/status/RegistryStatusChecker.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.registry.status;
+
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * RegistryStatusChecker
+ *
+ * @author william.liangf
+ */
+@Activate
+public class RegistryStatusChecker implements StatusChecker {
+
+ public Status check() {
+ Collection<Registry> regsitries = AbstractRegistryFactory.getRegistries();
+ if (regsitries == null || regsitries.size() == 0) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Status.Level level = Status.Level.OK;
+ StringBuilder buf = new StringBuilder();
+ for (Registry registry : regsitries) {
+ if (buf.length() > 0) {
+ buf.append(",");
+ }
+ buf.append(registry.getUrl().getAddress());
+ if (! registry.isAvailable()) {
+ level = Status.Level.ERROR;
+ buf.append("(disconnected)");
+ } else {
+ buf.append("(connected)");
+ }
+ }
+ return new Status(level, buf.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.java
new file mode 100644
index 0000000..5fc6f95
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+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.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.UrlUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+
+/**
+ * AbstractRegistry. (SPI, Prototype, ThreadSafe)
+ *
+ * @author chao.liuc
+ * @author william.liangf
+ */
+public abstract class AbstractRegistry implements Registry {
+
+ // 日志输出
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ // URL地址分隔符,用于文件缓存中,服务提供者URL分隔
+ private static final char URL_SEPARATOR = ' ';
+
+ // URL地址分隔正则表达式,用于解析文件缓存中服务提供者URL列表
+ private static final String URL_SPLIT = "\\s+";
+
+ private URL registryUrl;
+
+ // 本地磁盘缓存文件
+ private File file;
+
+ // 本地磁盘缓存,其中特殊的key值.registies记录注册中心列表,其它均为notified服务提供者列表
+ private final Properties properties = new Properties();
+
+ // 文件缓存定时写入
+ private final ExecutorService registryCacheExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveRegistryCache", true));
+
+ //是否是同步保存文件
+ private final boolean syncSaveFile ;
+
+ private final AtomicLong lastCacheChanged = new AtomicLong();
+
+ private final Set<URL> registered = new ConcurrentHashSet<URL>();
+
+ private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
+
+ private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<URL, Map<String, List<URL>>>();
+
+ public AbstractRegistry(URL url) {
+ setUrl(url);
+ // 启动文件保存定时器
+ syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
+ String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getHost() + ".cache");
+ File file = null;
+ if (ConfigUtils.isNotEmpty(filename)) {
+ file = new File(filename);
+ if(! file.exists() && file.getParentFile() != null && ! file.getParentFile().exists()){
+ if(! file.getParentFile().mkdirs()){
+ throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
+ }
+ }
+ }
+ this.file = file;
+ loadProperties();
+ }
+
+ protected void setUrl(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("registry url == null");
+ }
+ this.registryUrl = url;
+ }
+
+ public URL getUrl() {
+ return registryUrl;
+ }
+
+ public Set<URL> getRegistered() {
+ return registered;
+ }
+
+ public Map<URL, Set<NotifyListener>> getSubscribed() {
+ return subscribed;
+ }
+
+ public Map<URL, Map<String, List<URL>>> getNotified() {
+ return notified;
+ }
+
+ public File getCacheFile() {
+ return file;
+ }
+
+ public Properties getCacheProperties() {
+ return properties;
+ }
+
+ public AtomicLong getLastCacheChanged(){
+ return lastCacheChanged;
+ }
+
+ private class SaveProperties implements Runnable{
+ private long version;
+ private SaveProperties(long version){
+ this.version = version;
+ }
+ public void run() {
+ doSaveProperties(version);
+ }
+ }
+
+ public void doSaveProperties(long version) {
+ if(version < lastCacheChanged.get()){
+ return;
+ }
+ if (file == null) {
+ return;
+ }
+ Properties newProperties = new Properties();
+ // 保存之前先读取一遍,防止多个注册中心之间冲突
+ InputStream in = null;
+ try {
+ if (file.exists()) {
+ in = new FileInputStream(file);
+ newProperties.load(in);
+ }
+ } catch (Throwable e) {
+ logger.warn("Failed to load registry store file, cause: " + e.getMessage(), e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ // 保存
+ try {
+ newProperties.putAll(properties);
+ File lockfile = new File(file.getAbsolutePath() + ".lock");
+ if (!lockfile.exists()) {
+ lockfile.createNewFile();
+ }
+ RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
+ try {
+ FileChannel channel = raf.getChannel();
+ try {
+ FileLock lock = channel.tryLock();
+ if (lock == null) {
+ throw new IOException("Can not lock the registry cache file " + file.getAbsolutePath() + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties");
+ }
+ // 保存
+ try {
+ if (! file.exists()) {
+ file.createNewFile();
+ }
+ FileOutputStream outputFile = new FileOutputStream(file);
+ try {
+ newProperties.store(outputFile, "Dubbo Registry Cache");
+ } finally {
+ outputFile.close();
+ }
+ } finally {
+ lock.release();
+ }
+ } finally {
+ channel.close();
+ }
+ } finally {
+ raf.close();
+ }
+ } catch (Throwable e) {
+ if (version < lastCacheChanged.get()) {
+ return;
+ } else {
+ registryCacheExecutor.execute(new SaveProperties(lastCacheChanged.incrementAndGet()));
+ }
+ logger.warn("Failed to save registry store file, cause: " + e.getMessage(), e);
+ }
+ }
+
+ private void loadProperties() {
+ if (file != null && file.exists()) {
+ InputStream in = null;
+ try {
+ in = new FileInputStream(file);
+ properties.load(in);
+ if (logger.isInfoEnabled()) {
+ logger.info("Load registry store file " + file + ", data: " + properties);
+ }
+ } catch (Throwable e) {
+ logger.warn("Failed to load registry store file " + file, e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ }
+ }
+
+ public List<URL> getCacheUrls(URL url) {
+ for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+ String key = (String) entry.getKey();
+ String value = (String) entry.getValue();
+ if (key != null && key.length() > 0 && key.equals(url.getServiceKey())
+ && (Character.isLetter(key.charAt(0)) || key.charAt(0) == '_')
+ && value != null && value.length() > 0) {
+ String[] arr = value.trim().split(URL_SPLIT);
+ List<URL> urls = new ArrayList<URL>();
+ for (String u : arr) {
+ urls.add(URL.valueOf(u));
+ }
+ return urls;
+ }
+ }
+ return null;
+ }
+
+ public List<URL> lookup(URL url) {
+ List<URL> result = new ArrayList<URL>();
+ Map<String, List<URL>> notifiedUrls = getNotified().get(url);
+ if (notifiedUrls != null && notifiedUrls.size() > 0) {
+ for (List<URL> urls : notifiedUrls.values()) {
+ for (URL u : urls) {
+ if (! Constants.EMPTY_PROTOCOL.equals(u.getProtocol())) {
+ result.add(u);
+ }
+ }
+ }
+ } else {
+ final AtomicReference<List<URL>> reference = new AtomicReference<List<URL>>();
+ NotifyListener listener = new NotifyListener() {
+ public void notify(List<URL> urls) {
+ reference.set(urls);
+ }
+ };
+ subscribe(url, listener); // 订阅逻辑保证第一次notify后再返回
+ List<URL> urls = reference.get();
+ if (urls != null && urls.size() > 0) {
+ for (URL u : urls) {
+ if (! Constants.EMPTY_PROTOCOL.equals(u.getProtocol())) {
+ result.add(u);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public void register(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("register url == null");
+ }
+ if (logger.isInfoEnabled()){
+ logger.info("Register: " + url);
+ }
+ registered.add(url);
+ }
+
+ public void unregister(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("unregister url == null");
+ }
+ if (logger.isInfoEnabled()){
+ logger.info("Unregister: " + url);
+ }
+ registered.remove(url);
+ }
+
+ public void subscribe(URL url, NotifyListener listener) {
+ if (url == null) {
+ throw new IllegalArgumentException("subscribe url == null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("subscribe listener == null");
+ }
+ if (logger.isInfoEnabled()){
+ logger.info("Subscribe: " + url);
+ }
+ Set<NotifyListener> listeners = subscribed.get(url);
+ if (listeners == null) {
+ subscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
+ listeners = subscribed.get(url);
+ }
+ 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);
+ }
+ Set<NotifyListener> listeners = subscribed.get(url);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+
+ protected void recover() throws Exception {
+ // register
+ Set<URL> recoverRegistered = new HashSet<URL>(getRegistered());
+ if (! recoverRegistered.isEmpty()) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Recover register url " + recoverRegistered);
+ }
+ for (URL url : recoverRegistered) {
+ register(url);
+ }
+ }
+ // subscribe
+ Map<URL, Set<NotifyListener>> recoverSubscribed = new HashMap<URL, Set<NotifyListener>>(getSubscribed());
+ if (! recoverSubscribed.isEmpty()) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Recover subscribe url " + recoverSubscribed.keySet());
+ }
+ for (Map.Entry<URL, Set<NotifyListener>> entry : recoverSubscribed.entrySet()) {
+ URL url = entry.getKey();
+ for (NotifyListener listener : entry.getValue()) {
+ subscribe(url, listener);
+ }
+ }
+ }
+ }
+
+ protected static List<URL> filterEmpty(URL url, List<URL> urls) {
+ if (urls == null || urls.size() == 0) {
+ List<URL> result = new ArrayList<URL>(1);
+ result.add(url.setProtocol(Constants.EMPTY_PROTOCOL));
+ return result;
+ }
+ return urls;
+ }
+
+ protected void notify(List<URL> urls) {
+ if(urls == null || urls.isEmpty()) return;
+
+ for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
+ URL url = entry.getKey();
+
+ if(! UrlUtils.isMatch(url, urls.get(0))) {
+ continue;
+ }
+
+ Set<NotifyListener> listeners = entry.getValue();
+ if (listeners != null) {
+ for (NotifyListener listener : listeners) {
+ try {
+ notify(url, listener, filterEmpty(url, urls));
+ } catch (Throwable t) {
+ logger.error("Failed to notify registry event, urls: " + urls + ", 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)
+ && ! Constants.ANY_VALUE.equals(url.getServiceInterface())) {
+ logger.warn("Ignore empty notify urls for subscribe url " + url);
+ return;
+ }
+ Map<String, List<URL>> result = new HashMap<String, List<URL>>();
+ for (URL u : urls) {
+ if (UrlUtils.isMatch(url, u)) {
+ String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
+ List<URL> categoryList = result.get(category);
+ if (categoryList == null) {
+ categoryList = new ArrayList<URL>();
+ result.put(category, categoryList);
+ }
+ categoryList.add(u);
+ }
+ }
+ if (result.size() == 0) {
+ return;
+ }
+ Map<String, List<URL>> categoryNotified = notified.get(url);
+ if (categoryNotified == null) {
+ notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
+ categoryNotified = notified.get(url);
+ }
+ for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
+ String category = entry.getKey();
+ List<URL> categoryList = entry.getValue();
+ categoryNotified.put(category, categoryList);
+ saveProperties(url);
+ listener.notify(categoryList);
+ }
+ }
+
+ private void saveProperties(URL url) {
+ if (file == null) {
+ return;
+ }
+
+ try {
+ StringBuilder buf = new StringBuilder();
+ Map<String, List<URL>> categoryNotified = notified.get(url);
+ if (categoryNotified != null) {
+ for (List<URL> us : categoryNotified.values()) {
+ for (URL u : us) {
+ if (buf.length() > 0) {
+ buf.append(URL_SEPARATOR);
+ }
+ buf.append(u.toFullString());
+ }
+ }
+ }
+ properties.setProperty(url.getServiceKey(), buf.toString());
+ long version = lastCacheChanged.incrementAndGet();
+ if (syncSaveFile) {
+ doSaveProperties(version);
+ } else {
+ registryCacheExecutor.execute(new SaveProperties(version));
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+
+ public void destroy() {
+ if (logger.isInfoEnabled()){
+ logger.info("Destroy registry:" + getUrl());
+ }
+ Set<URL> destroyRegistered = new HashSet<URL>(getRegistered());
+ if (! destroyRegistered.isEmpty()) {
+ for (URL url : new HashSet<URL>(getRegistered())) {
+ if (url.getParameter(Constants.DYNAMIC_KEY, true)) {
+ try {
+ unregister(url);
+ if (logger.isInfoEnabled()) {
+ logger.info("Destroy unregister url " + url);
+ }
+ } catch (Throwable t) {
+ logger.warn("Failed to unregister url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ }
+ Map<URL, Set<NotifyListener>> destroySubscribed = new HashMap<URL, Set<NotifyListener>>(getSubscribed());
+ if (! destroySubscribed.isEmpty()) {
+ for (Map.Entry<URL, Set<NotifyListener>> entry : destroySubscribed.entrySet()) {
+ URL url = entry.getKey();
+ for (NotifyListener listener : entry.getValue()) {
+ try {
+ unsubscribe(url, listener);
+ if (logger.isInfoEnabled()) {
+ logger.info("Destroy unsubscribe url " + url);
+ }
+ } catch (Throwable t) {
+ logger.warn("Failed to unsubscribe url " + url + " to registry " + getUrl() + " on destroy, cause: " +t.getMessage(), t);
+ }
+ }
+ }
+ }
+ }
+
+ public String toString() {
+ return getUrl().toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.java
new file mode 100644
index 0000000..ae2f7fe
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.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.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;
+
+/**
+ * AbstractRegistryFactory. (SPI, Singleton, 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);
+
+ // 注册中心获取过程锁
+ private static final ReentrantLock LOCK = new ReentrantLock();
+
+ // 注册中心集合 Map<RegistryAddress, Registry>
+ private static final Map<String, Registry> REGISTRIES = new ConcurrentHashMap<String, Registry>();
+
+ /**
+ * 获取所有注册中心
+ *
+ * @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();
+ }
+ }
+
+ public Registry getRegistry(URL url) {
+ // 锁定注册中心获取过程,保证注册中心单一实例
+ LOCK.lock();
+ try {
+ String key = url.getProtocol() + "://" + url.getUsername() + ":" + url.getPassword() + "@" + url.getIp() + ":" + url.getPort();
+ Registry registry = REGISTRIES.get(key);
+ if (registry != null) {
+ return registry;
+ }
+ registry = createRegistry(url);
+ if (registry == null) {
+ throw new IllegalStateException("Can not create registry " + url);
+ }
+ REGISTRIES.put(key, registry);
+ return registry;
+ } finally {
+ // 释放锁
+ LOCK.unlock();
+ }
+ }
+
+ protected abstract Registry createRegistry(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
new file mode 100644
index 0000000..8e3b682
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.registry.NotifyListener;
+
+/**
+ * FailbackRegistry. (SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public abstract class FailbackRegistry extends AbstractRegistry {
+
+ // 定时任务执行器
+ private final ScheduledExecutorService retryExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryFailedRetryTimer", true));
+
+ // 失败重试定时器,定时检查是否有请求失败,如有,无限次重试
+ private final ScheduledFuture<?> retryFuture;
+
+ private final Set<URL> failedRegistered = new ConcurrentHashSet<URL>();
+
+ private final Set<URL> failedUnregistered = new ConcurrentHashSet<URL>();
+
+ private final ConcurrentMap<URL, Set<NotifyListener>> failedSubscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
+
+ private final ConcurrentMap<URL, Set<NotifyListener>> failedUnsubscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
+
+ private final ConcurrentMap<URL, Map<NotifyListener, List<URL>>> failedNotified = new ConcurrentHashMap<URL, Map<NotifyListener, List<URL>>>();
+
+ public FailbackRegistry(URL url) {
+ super(url);
+ int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
+ this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
+ public void run() {
+ // 检测并连接注册中心
+ try {
+ retry();
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
+ }
+ }
+ }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
+ }
+
+ public Future<?> getRetryFuture() {
+ return retryFuture;
+ }
+
+ public Set<URL> getFailedRegistered() {
+ return failedRegistered;
+ }
+
+ public Set<URL> getFailedUnregistered() {
+ return failedUnregistered;
+ }
+
+ public Map<URL, Set<NotifyListener>> getFailedSubscribed() {
+ return failedSubscribed;
+ }
+
+ public Map<URL, Set<NotifyListener>> getFailedUnsubscribed() {
+ return failedUnsubscribed;
+ }
+
+ public Map<URL, Map<NotifyListener, List<URL>>> getFailedNotified() {
+ return failedNotified;
+ }
+
+ private void addFailedSubscribed(URL url, NotifyListener listener) {
+ Set<NotifyListener> listeners = failedSubscribed.get(url);
+ if (listeners == null) {
+ failedSubscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
+ listeners = failedSubscribed.get(url);
+ }
+ listeners.add(listener);
+ }
+
+ private void removeFailedSubscribed(URL url, NotifyListener listener) {
+ Set<NotifyListener> listeners = failedSubscribed.get(url);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ listeners = failedUnsubscribed.get(url);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ Map<NotifyListener, List<URL>> notified = failedNotified.get(url);
+ if (notified != null) {
+ notified.remove(listener);
+ }
+ }
+
+ @Override
+ public void register(URL url) {
+ super.register(url);
+ failedRegistered.remove(url);
+ failedUnregistered.remove(url);
+ try {
+ // 向服务器端发送注册请求
+ doRegister(url);
+ } catch (Exception t) {
+ // 将失败的注册请求记录到失败列表,定时重试
+ failedRegistered.add(url);
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)
+ && url.getParameter(Constants.CHECK_KEY, true)
+ && ! Constants.CONSUMER_PROTOCOL.equals(url.getProtocol())) { // 如果开启了启动时检测,则直接抛出异常
+ throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
+ } else {
+ logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+
+ @Override
+ public void unregister(URL url) {
+ super.unregister(url);
+ failedRegistered.remove(url);
+ failedUnregistered.remove(url);
+ try {
+ // 向服务器端发送取消注册请求
+ doUnregister(url);
+ } catch (Exception t) {
+ // 将失败的取消注册请求记录到失败列表,定时重试
+ failedUnregistered.add(url);
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)
+ && url.getParameter(Constants.CHECK_KEY, true)
+ && ! Constants.CONSUMER_PROTOCOL.equals(url.getProtocol())) { // 如果开启了启动时检测,则直接抛出异常
+ throw new IllegalStateException("Failed to unregister " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
+ } else {
+ logger.error("Failed to uregister " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+
+ @Override
+ public void subscribe(URL url, NotifyListener listener) {
+ super.subscribe(url, listener);
+ removeFailedSubscribed(url, listener);
+ try {
+ // 向服务器端发送订阅请求
+ doSubscribe(url, listener);
+ } catch (Exception t) {
+ // 将失败的订阅请求记录到失败列表,定时重试
+ addFailedSubscribed(url, listener);
+ List<URL> urls = getCacheUrls(url);
+ if (urls != null && urls.size() > 0) {
+ notify(url, listener, urls);
+ logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
+ } else {
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)
+ && url.getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常
+ throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
+ } else {
+ logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void unsubscribe(URL url, NotifyListener listener) {
+ super.unsubscribe(url, listener);
+ removeFailedSubscribed(url, listener);
+ try {
+ // 向服务器端发送取消订阅请求
+ doUnsubscribe(url, listener);
+ } catch (Exception t) {
+ // 将失败的取消订阅请求记录到失败列表,定时重试
+ Set<NotifyListener> listeners = failedUnsubscribed.get(url);
+ if (listeners == null) {
+ failedUnsubscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
+ listeners = failedUnsubscribed.get(url);
+ }
+ listeners.add(listener);
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)
+ && url.getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常
+ throw new IllegalStateException("Failed to unsubscribe " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
+ } else {
+ logger.error("Failed to unsubscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+
+ @Override
+ 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");
+ }
+ try {
+ super.notify(url, listener, urls);
+ } catch (Exception t) {
+ // 将失败的通知请求记录到失败列表,定时重试
+ Map<NotifyListener, List<URL>> listeners = failedNotified.get(url);
+ if (listeners == null) {
+ failedNotified.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, List<URL>>());
+ listeners = failedNotified.get(url);
+ }
+ listeners.put(listener, urls);
+ logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+
+ @Override
+ protected void recover() throws Exception {
+ // register
+ Set<URL> recoverRegistered = new HashSet<URL>(getRegistered());
+ if (! recoverRegistered.isEmpty()) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Recover register url " + recoverRegistered);
+ }
+ for (URL url : recoverRegistered) {
+ failedRegistered.add(url);
+ }
+ }
+ // subscribe
+ Map<URL, Set<NotifyListener>> recoverSubscribed = new HashMap<URL, Set<NotifyListener>>(getSubscribed());
+ if (! recoverSubscribed.isEmpty()) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Recover subscribe url " + recoverSubscribed.keySet());
+ }
+ for (Map.Entry<URL, Set<NotifyListener>> entry : recoverSubscribed.entrySet()) {
+ URL url = entry.getKey();
+ for (NotifyListener listener : entry.getValue()) {
+ addFailedSubscribed(url, listener);
+ }
+ }
+ }
+ }
+
+ // 重试失败的动作
+ protected void retry() {
+ if (! failedRegistered.isEmpty()) {
+ Set<URL> failed = new HashSet<URL>(failedRegistered);
+ if (failed.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Retry register " + failed);
+ }
+ try {
+ for (URL url : failed) {
+ try {
+ doRegister(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<URL> failed = new HashSet<URL>(failedUnregistered);
+ if (failed.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Retry unregister " + failed);
+ }
+ try {
+ for (URL url : failed) {
+ try {
+ doUnregister(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<URL, Set<NotifyListener>> failed = new HashMap<URL, Set<NotifyListener>>(failedSubscribed);
+ for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, Set<NotifyListener>>(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<URL, Set<NotifyListener>> entry : failed.entrySet()) {
+ URL url = 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<URL, Set<NotifyListener>> failed = new HashMap<URL, Set<NotifyListener>>(failedUnsubscribed);
+ for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, Set<NotifyListener>>(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<URL, Set<NotifyListener>> entry : failed.entrySet()) {
+ URL url = 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<URL, Map<NotifyListener, List<URL>>> failed = new HashMap<URL, Map<NotifyListener, List<URL>>>(failedNotified);
+ for (Map.Entry<URL, Map<NotifyListener, List<URL>>> entry : new HashMap<URL, Map<NotifyListener, List<URL>>>(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);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ try {
+ retryFuture.cancel(true);
+ } catch (Throwable t) {
+ logger.warn(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);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..f519702
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1 @@
+registry=com.alibaba.dubbo.registry.status.RegistryStatusChecker
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..8e75ea0
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,3 @@
+registries=com.alibaba.dubbo.registry.pages.RegistriesPageHandler
+registered=com.alibaba.dubbo.registry.pages.RegisteredPageHandler
+subscribed=com.alibaba.dubbo.registry.pages.SubscribedPageHandler
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..e499fe3
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/PerformanceRegistryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/PerformanceRegistryTest.java
new file mode 100644
index 0000000..0ef8a37
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/PerformanceRegistryTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+
+/**
+ * RegistryPerformanceTest
+ *
+ * @author william.liangf
+ */
+public class PerformanceRegistryTest extends TestCase {
+
+ private static final Logger logger = LoggerFactory.getLogger(PerformanceRegistryTest.class);
+
+ @Test
+ public void testRegistry() {
+ // 读取参数
+ if (PerformanceUtils.getProperty("server", null) == null) {
+ logger.warn("Please set -Dserver=127.0.0.1:9090");
+ return;
+ }
+ final int base = PerformanceUtils.getIntProperty("base", 0);
+ final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100);
+ int r = PerformanceUtils.getIntProperty("runs", 1000);
+ final int runs = r > 0 ? r : Integer.MAX_VALUE;
+ final Registry registry = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(URL.valueOf("remote://admin:hello1234@" + PerformanceUtils.getProperty("server", "10.20.153.28:9090")));
+ for (int i = 0; i < concurrent; i ++) {
+ final int t = i;
+ new Thread(new Runnable() {
+ public void run() {
+ for (int j = 0; j < runs; j ++) {
+ registry.register(URL.valueOf("remote://" + NetUtils.getLocalHost() + ":8080/demoService" + t + "_" + j + "?version=1.0.0&application=demo&dubbo=2.0&interface=" + "com.alibaba.dubbo.demo.DemoService" + (base + t) + "_" + (base + j)));
+ }
+ }
+ }).start();
+ }
+ synchronized (PerformanceRegistryTest.class) {
+ while (true) {
+ try {
+ PerformanceRegistryTest.class.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/PerformanceUtils.java b/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/PerformanceUtils.java
new file mode 100644
index 0000000..1e6683c
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/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/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/RegistryTestSupport.java b/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/RegistryTestSupport.java
new file mode 100644
index 0000000..d5ae6c0
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/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/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactoryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactoryTest.java
new file mode 100644
index 0000000..ffa79e2
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactoryTest.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.support;
+
+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.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+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/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/support/FailbackRegistryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/support/FailbackRegistryTest.java
new file mode 100644
index 0000000..07b9b5e
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/com/alibaba/dubbo/registry/support/FailbackRegistryTest.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 1999-2101 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.CollectionUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+
+/**
+ *
+ * @author liuchao
+ */
+public class FailbackRegistryTest {
+ MockRegistry registry;
+ static String service;
+ static URL serviceUrl;
+ static URL registryUrl;
+ private int FAILED_PERIOD = 200;
+ private int sleeptime = 100;
+ private int trytimes = 5;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ service = "com.alibaba.dubbo.test.DemoService";
+ serviceUrl = URL.valueOf("remote://127.0.0.1/demoservice?method=get");
+ registryUrl = URL.valueOf("http://1.2.3.4:9090/registry?check=false&file=N/A").addParameter(Constants.REGISTRY_RETRY_PERIOD_KEY,String.valueOf(FAILED_PERIOD));
+ }
+
+ /**
+ * Test method for
+ * {@link com.alibaba.dubbo.registry.internal.FailbackRegistry#doRetry()}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testDoRetry() throws Exception {
+
+ final AtomicReference<Boolean> notified = new AtomicReference<Boolean>(false);
+ final CountDownLatch latch = new CountDownLatch(3);//全部共调用3次。成功才会减1. subscribe register的失败尝试不会在做了
+
+ NotifyListener listner = new NotifyListener() {
+ public void notify(List<URL> urls) {
+ notified.set(Boolean.TRUE);
+ }
+ };
+ registry = new MockRegistry(registryUrl, latch);
+ registry.setBad(true);
+ registry.register(serviceUrl);
+ registry.unregister(serviceUrl);
+ registry.subscribe(serviceUrl.setProtocol(Constants.CONSUMER_PROTOCOL).addParameters(CollectionUtils.toStringMap("check", "false")), listner);
+ registry.unsubscribe(serviceUrl.setProtocol(Constants.CONSUMER_PROTOCOL).addParameters(CollectionUtils.toStringMap("check", "false")), listner);
+
+ //失败的情况不能调用到listener.
+ assertEquals(false, notified.get());
+ assertEquals(3, latch.getCount());
+
+ registry.setBad(false);
+
+ for (int i = 0; i < trytimes; i++) {
+ System.out.println("failback registry retry ,times:" + i);
+ //System.out.println(latch.getCount());
+ if (latch.getCount() == 0)
+ break;
+ Thread.sleep(sleeptime);
+ }
+// Thread.sleep(100000);//for debug
+ assertEquals(0, latch.getCount());
+ //unsubscribe时会清除failedsubcribe对应key
+ assertEquals(false, notified.get());
+ }
+
+ @Test
+ public void testDoRetry_subscribe() throws Exception {
+
+ final CountDownLatch latch = new CountDownLatch(1);//全部共调用4次。成功才会减1. subscribe的失败尝试不会在做了
+
+ registry = new MockRegistry(registryUrl, latch);
+ registry.setBad(true);
+ registry.register(serviceUrl);
+
+ registry.setBad(false);
+
+ for (int i = 0; i < trytimes; i++) {
+ System.out.println("failback registry retry ,times:" + i);
+ if (latch.getCount() == 0)
+ break;
+ Thread.sleep(sleeptime);
+ }
+ assertEquals(0, latch.getCount());
+ }
+
+ @Test
+ public void testDoRetry_register() throws Exception {
+
+ final AtomicReference<Boolean> notified = new AtomicReference<Boolean>(false);
+ final CountDownLatch latch = new CountDownLatch(1);//全部共调用4次。成功才会减1. subscribe的失败尝试不会在做了
+
+ NotifyListener listner = new NotifyListener() {
+ public void notify(List<URL> urls) {
+ notified.set(Boolean.TRUE);
+ }
+ };
+ registry = new MockRegistry(registryUrl, latch);
+ registry.setBad(true);
+ registry.subscribe(serviceUrl.setProtocol(Constants.CONSUMER_PROTOCOL).addParameters(CollectionUtils.toStringMap("check", "false")), listner);
+
+ //失败的情况不能调用到listener.
+ assertEquals(false, notified.get());
+ assertEquals(1, latch.getCount());
+
+ registry.setBad(false);
+
+ for (int i = 0; i < trytimes; i++) {
+ System.out.println("failback registry retry ,times:" + i);
+ //System.out.println(latch.getCount());
+ if (latch.getCount() == 0)
+ break;
+ Thread.sleep(sleeptime);
+ }
+// Thread.sleep(100000);
+ assertEquals(0, latch.getCount());
+ //unsubscribe时会清除failedsubcribe对应key
+ assertEquals(true, notified.get());
+ }
+
+ @Test
+ public void testDoRetry_nofify() throws Exception {
+
+ //初始值0
+ final AtomicInteger count = new AtomicInteger(0);
+
+ NotifyListener listner = new NotifyListener() {
+ public void notify(List<URL> urls) {
+ count.incrementAndGet();
+ //第一次抛出异常,看后面是否会再次调用到incrementAndGet
+ if(count.get() == 1l ){
+ throw new RuntimeException("test exception please ignore");
+ }
+ }
+ };
+ registry = new MockRegistry(registryUrl, new CountDownLatch(0));
+ registry.subscribe(serviceUrl.setProtocol(Constants.CONSUMER_PROTOCOL).addParameters(CollectionUtils.toStringMap("check", "false")), listner);
+
+ assertEquals(1, count.get()); //确保subscribe调用完成后刚调用过一次count.incrementAndGet
+ //等定时器.
+ for (int i = 0; i < trytimes; i++) {
+ System.out.println("failback notify retry ,times:" + i);
+ if (count.get() == 2)
+ break;
+ Thread.sleep(sleeptime);
+ }
+ assertEquals(2, count.get());
+ }
+
+
+
+ private static class MockRegistry extends FailbackRegistry {
+ CountDownLatch latch;
+
+ /**
+ * @param url
+ */
+ public MockRegistry(URL url, CountDownLatch latch) {
+ super(url);
+ this.latch = latch;
+ }
+
+ private boolean bad = false;
+
+ /**
+ * @param bad the bad to set
+ */
+ public void setBad(boolean bad) {
+ this.bad = bad;
+ }
+
+ @Override
+ protected void doRegister(URL url) {
+ if (bad) {
+ throw new RuntimeException("can not invoke!");
+ }
+ //System.out.println("do doRegister");
+ latch.countDown();
+
+ }
+
+ @Override
+ protected void doUnregister(URL url) {
+ if (bad) {
+ throw new RuntimeException("can not invoke!");
+ }
+ //System.out.println("do doUnregister");
+ latch.countDown();
+
+ }
+
+ @Override
+ protected void doSubscribe(URL url, NotifyListener listener) {
+ if (bad) {
+ throw new RuntimeException("can not invoke!");
+ }
+ //System.out.println("do doSubscribe");
+ super.notify(url, listener, Arrays.asList(new URL[] { serviceUrl }));
+ latch.countDown();
+ }
+
+ @Override
+ protected void doUnsubscribe(URL url, NotifyListener listener) {
+ if (bad) {
+ throw new RuntimeException("can not invoke!");
+ }
+ //System.out.println("do doUnsubscribe");
+ latch.countDown();
+ }
+
+ @Override
+ protected void retry() {
+ super.retry();
+ if (bad) {
+ throw new RuntimeException("can not invoke!");
+ }
+ //System.out.println("do retry");
+ latch.countDown();
+ }
+
+ public boolean isAvailable() {
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/test/resources/log4j.xml b/dubbo-registry/dubbo-registry-api/src/test/resources/log4j.xml
new file mode 100644
index 0000000..1eb50f0
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/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-registry/dubbo-registry-default/pom.xml b/dubbo-registry/dubbo-registry-default/pom.xml
new file mode 100644
index 0000000..f596c57
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/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-registry</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-registry-default</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</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-rpc-injvm</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/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java b/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
new file mode 100644
index 0000000..fb8790f
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.dubbo;
+
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.registry.support.FailbackRegistry;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * DubboRegistry
+ *
+ * @author william.liangf
+ */
+public class DubboRegistry extends FailbackRegistry {
+
+ private final static Logger logger = LoggerFactory.getLogger(DubboRegistry.class);
+
+ // 重连检测周期3秒(单位毫秒)
+ private static final int RECONNECT_PERIOD_DEFAULT = 3 * 1000;
+
+ // 定时任务执行器
+ private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryReconnectTimer", true));
+
+ // 重连定时器,定时检查连接是否可用,不可用时,无限次重连
+ private final ScheduledFuture<?> reconnectFuture;
+
+ // 客户端获取过程锁,锁定客户端实例的创建过程,防止重复的客户端
+ private final ReentrantLock clientLock = new ReentrantLock();
+
+ private final Invoker<RegistryService> registryInvoker;
+
+ private final RegistryService registryService;
+
+ public DubboRegistry(Invoker<RegistryService> registryInvoker, RegistryService registryService) {
+ super(registryInvoker.getUrl());
+ this.registryInvoker = registryInvoker;
+ this.registryService = registryService;
+ // 启动重连定时器
+ int reconnectPeriod = registryInvoker.getUrl().getParameter(Constants.REGISTRY_RECONNECT_PERIOD_KEY, RECONNECT_PERIOD_DEFAULT);
+ reconnectFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
+ public void run() {
+ // 检测并连接注册中心
+ try {
+ connect();
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected error occur at reconnect, cause: " + t.getMessage(), t);
+ }
+ }
+ }, reconnectPeriod, reconnectPeriod, TimeUnit.MILLISECONDS);
+ }
+
+ protected final void connect() {
+ try {
+ // 检查是否已连接
+ if (isAvailable()) {
+ return;
+ }
+ if (logger.isInfoEnabled()) {
+ logger.info("Reconnect to registry " + getUrl());
+ }
+ clientLock.lock();
+ try {
+ // 双重检查是否已连接
+ if (isAvailable()) {
+ return;
+ }
+ recover();
+ } finally {
+ clientLock.unlock();
+ }
+ } catch (Throwable t) { // 忽略所有异常,等待下次重试
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)) {
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ }
+ throw new RuntimeException(t.getMessage(), t);
+ }
+ logger.error("Failed to connect to registry " + getUrl().getAddress() + " from provider/consumer " + NetUtils.getLocalHost() + " use dubbo " + Version.getVersion() + ", cause: " + t.getMessage(), t);
+ }
+ }
+
+ public boolean isAvailable() {
+ if (registryInvoker == null)
+ return false;
+ return registryInvoker.isAvailable();
+ }
+
+ public void destroy() {
+ super.destroy();
+ try {
+ // 取消重连定时器
+ if (! reconnectFuture.isCancelled()) {
+ reconnectFuture.cancel(true);
+ }
+ } catch (Throwable t) {
+ logger.warn("Failed to cancel reconnect timer", t);
+ }
+ registryInvoker.destroy();
+ }
+
+ protected void doRegister(URL url) {
+ registryService.register(url);
+ }
+
+ 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/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.java b/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.java
new file mode 100644
index 0000000..d11faf3
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.dubbo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.registry.integration.RegistryDirectory;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+
+/**
+ * DubboRegistryFactory
+ *
+ * @author william.liangf
+ */
+public class DubboRegistryFactory extends AbstractRegistryFactory {
+
+ private Protocol protocol;
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ private ProxyFactory proxyFactory;
+
+ public void setProxyFactory(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ private Cluster cluster;
+
+ public void setCluster(Cluster cluster) {
+ this.cluster = cluster;
+ }
+
+ public Registry createRegistry(URL url) {
+ url = getRegistryURL(url);
+ List<URL> urls = new ArrayList<URL>();
+ urls.add(url.removeParameter(Constants.BACKUP_KEY));
+ String backup = url.getParameter(Constants.BACKUP_KEY);
+ if (backup != null && backup.length() > 0) {
+ String[] addresses = Constants.COMMA_SPLIT_PATTERN.split(backup);
+ for (String address : addresses) {
+ urls.add(url.setAddress(address));
+ }
+ }
+ RegistryDirectory<RegistryService> directory = new RegistryDirectory<RegistryService>(RegistryService.class, url.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName()).addParameterAndEncoded(Constants.REFER_KEY, url.toParameterString()));
+ Invoker<RegistryService> registryInvoker = cluster.join(directory);
+ RegistryService registryService = proxyFactory.getProxy(registryInvoker);
+ DubboRegistry registry = new DubboRegistry(registryInvoker, registryService);
+ directory.setRegistry(registry);
+ directory.setProtocol(protocol);
+ directory.notify(urls);
+ directory.subscribe(new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, RegistryService.class.getName(), url.getParameters()));
+ return registry;
+ }
+
+ private static URL getRegistryURL(URL url) {
+ return url.setPath(RegistryService.class.getName())
+ .removeParameter(Constants.EXPORT_KEY).removeParameter(Constants.REFER_KEY)
+ .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
+ .addParameter(Constants.CLUSTER_STICKY_KEY, "true")
+ .addParameter(Constants.LAZY_CONNECT_KEY, "true")
+ .addParameter(Constants.RECONNECT_KEY, "false")
+ .addParameterIfAbsent(Constants.TIMEOUT_KEY, "10000")
+ .addParameterIfAbsent(Constants.CALLBACK_INSTANCES_LIMIT_KEY, "10000")
+ .addParameterIfAbsent(Constants.CONNECT_TIMEOUT_KEY, "10000")
+ .addParameter(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(RegistryService.class).getDeclaredMethodNames())), ","))
+ //.addParameter(Constants.STUB_KEY, RegistryServiceStub.class.getName())
+ //.addParameter(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString()) //for event dispatch
+ //.addParameter(Constants.ON_DISCONNECT_KEY, "disconnect")
+ .addParameter("subscribe.1.callback", "true")
+ .addParameter("unsubscribe.1.callback", "false");
+ }
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry/dubbo-registry-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..55d739d
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+dubbo=com.alibaba.dubbo.registry.dubbo.DubboRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/AbstractRegistryService.java b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/AbstractRegistryService.java
new file mode 100644
index 0000000..28305b5
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/AbstractRegistryService.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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) {
+ URL deleteURL = null;
+ for (URL u : urls) {
+ if (u.toIdentityString().equals(url.toIdentityString())) {
+ deleteURL = u;
+ break;
+ }
+ }
+ if (deleteURL != null) {
+ urls.remove(deleteURL);
+ }
+ }
+ }
+
+ public void subscribe(String service, URL url, NotifyListener listener) {
+ if (service == null) {
+ throw new IllegalArgumentException("service == null");
+ }
+ if (url == null) {
+ throw new IllegalArgumentException("parameters == null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener == null");
+ }
+ subscribed.put(service, url.getParameters());
+ addListener(service, listener);
+ }
+
+ public void unsubscribe(String service, URL url, NotifyListener listener) {
+ if (service == null) {
+ throw new IllegalArgumentException("service == null");
+ }
+ if (url == null) {
+ throw new IllegalArgumentException("parameters == null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener == null");
+ }
+ subscribed.remove(service);
+ removeListener(service, listener);
+ }
+
+ //consumer 与 provider的 listener可以一起存储,都是根据服务名称共享
+ private void addListener(final String service, final NotifyListener listener){
+ if (listener == null) {
+ return;
+ }
+ List<NotifyListener> listeners = notifyListeners.get(service);
+ if (listeners == null) {
+ notifyListeners.putIfAbsent(service, new CopyOnWriteArrayList<NotifyListener>());
+ listeners = notifyListeners.get(service);
+ }
+ if (listeners != null && !listeners.contains(listener)){
+ listeners.add(listener);
+ }
+ }
+
+ private void removeListener(final String service, final NotifyListener listener){
+ if (listener == null) {
+ return;
+ }
+ List<NotifyListener> listeners = notifyListeners.get(service);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+
+ private void doNotify(String service, List<URL> urls) {
+ notified.put(service, urls);
+ List<NotifyListener> listeners = notifyListeners.get(service);
+ if (listeners != null) {
+ for (NotifyListener listener : listeners) {
+ try {
+ notify(service, urls, listener);
+ } catch (Throwable t) {
+ logger.error("Failed to notify registry event, service: " + service + ", urls: " + urls + ", cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ }
+
+ protected void notify(String service, List<URL> urls, NotifyListener listener) {
+ listener.notify(urls);
+ }
+
+ protected final void forbid(String service) {
+ doNotify(service, new ArrayList<URL>(0));
+ }
+
+ protected final void notify(String service, List<URL> urls) {
+ if (service == null || service.length() == 0
+ || urls == null || urls.size() == 0) {
+ return;
+ }
+ doNotify(service, urls);
+ }
+
+ public Map<String, List<URL>> getRegistered() {
+ return Collections.unmodifiableMap(registered);
+ }
+
+ public List<URL> getRegistered(String service) {
+ return Collections.unmodifiableList(registered.get(service));
+ }
+
+ public Map<String, Map<String, String>> getSubscribed() {
+ return Collections.unmodifiableMap(subscribed);
+ }
+
+ public Map<String, String> getSubscribed(String service) {
+ return subscribed.get(service);
+ }
+
+ public Map<String, List<URL>> getNotified() {
+ return Collections.unmodifiableMap(notified);
+ }
+
+ public List<URL> getNotified(String service) {
+ return Collections.unmodifiableList(notified.get(service));
+ }
+
+ public Map<String, List<NotifyListener>> getListeners() {
+ return Collections.unmodifiableMap(notifyListeners);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/DemoService.java b/dubbo-registry/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/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/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockChannel.java b/dubbo-registry/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/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/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.java b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.java
new file mode 100644
index 0000000..0d677d8
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.dubbo;
+
+import java.net.InetSocketAddress;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ResponseCallback;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * MockedClient
+ *
+ * @author william.liangf
+ */
+public class MockedClient implements ExchangeClient {
+
+ //private String host;
+
+ //private int port;
+
+ private boolean connected;
+
+ private Object received;
+
+ private Object sent;
+
+ private Object invoked;
+
+ private Replier<?> handler;
+
+ private InetSocketAddress address;
+
+ private boolean closed = false;
+
+ //private ChannelListener listener;
+
+ public MockedClient(String host, int port, boolean connected) {
+ this(host, port, connected, null);
+ }
+
+ public MockedClient(String host, int port, boolean connected, Object received) {
+ this.address = new InetSocketAddress(host, port);
+ this.connected = connected;
+ this.received = received;
+ }
+
+ public void open() {
+ }
+
+ public void close() {
+ this.closed = true;
+ }
+
+ public void send(Object msg) throws RemotingException {
+ this.sent = msg;
+ }
+
+ public ResponseFuture request(Object msg) throws RemotingException {
+ return request(msg, 0);
+ }
+
+ public ResponseFuture request(Object msg, int timeout) throws RemotingException {
+ this.invoked = msg;
+ return new ResponseFuture() {
+ public Object get() throws RemotingException {
+ return received;
+ }
+ public Object get(int timeoutInMillis) throws RemotingException {
+ return received;
+ }
+ public boolean isDone() {
+ return true;
+ }
+ public void setCallback(ResponseCallback callback) {
+ }
+ };
+ }
+
+ public void registerHandler(Replier<?> handler) {
+ this.handler = handler;
+ }
+
+ public void unregisterHandler(Replier<?> handler) {
+ //this.handler = null;
+ }
+
+ public void addChannelListener(ChannelHandler listener) {
+ //this.listener = listener;
+ }
+
+ public void removeChannelListener(ChannelHandler listener) {
+ //this.listener = null;
+ }
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ public Object getSent() {
+ return sent;
+ }
+
+ public Replier<?> getHandler() {
+ return handler;
+ }
+
+ public Object getInvoked() {
+ return invoked;
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return address;
+ }
+
+ public String getName() {
+ return "mocked";
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return null;
+ }
+
+ public void setTimeout(int timeout) {
+ }
+
+ public int getTimeout() {
+ return 0;
+ }
+
+ public void close(int timeout) {
+ close();
+ }
+
+ public boolean isOpen() {
+ return closed;
+ }
+
+ public Codec getCodec() {
+ return null;
+ }
+
+ public void setCodec(Codec codec) {
+ }
+
+ public void setHost(String host) {
+ }
+
+ public String getHost() {
+ return null;
+ }
+
+ public void setPort(int port) {
+ }
+
+ public int getPort() {
+ return 0;
+ }
+
+ public void setThreadCount(int threadCount) {
+ }
+
+ public int getThreadCount() {
+ return 0;
+ }
+
+ public URL getUrl() {
+ return null;
+ }
+
+ public Replier<?> getReceiver() {
+ return null;
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return null;
+ }
+
+ public void reset(Map<String, String> parameters) {
+ }
+
+ public Channel getChannel() {
+ return this;
+ }
+
+ public ExchangeHandler getExchangeHandler() {
+ return null;
+ }
+
+ public void reconnect() throws RemotingException {
+ }
+
+ public Object getAttribute(String key) {
+ return null;
+ }
+
+ public void setAttribute(String key, Object value) {
+
+ }
+
+ public boolean hasAttribute(String key) {
+ return false;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ public void removeAttribute(String key) {
+
+ }
+ /**
+ * @return the received
+ */
+ public Object getReceived() {
+ return received;
+ }
+
+ /**
+ * @param received the received to set
+ */
+ public void setReceived(Object received) {
+ this.received = received;
+ }
+
+ /**
+ * @param connected the connected to set
+ */
+ public void setConnected(boolean connected) {
+ this.connected = connected;
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ }
+
+ public void reset(URL url) {
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters) {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
new file mode 100644
index 0000000..cca1ab8
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
@@ -0,0 +1,1071 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.dubbo;
+
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+import javax.script.ScriptEngineManager;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.LogUtil;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.registry.integration.RegistryDirectory;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;
+import com.alibaba.dubbo.rpc.cluster.router.script.ScriptRouter;
+import com.alibaba.dubbo.rpc.cluster.router.script.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+"?refer=" + URL.encode("interface="+service));
+ URL SERVICEURL = URL.valueOf("dubbo://127.0.0.1:9091/" + service + "?lazy=true");
+ URL SERVICEURL2 = URL.valueOf("dubbo://127.0.0.1:9092/" + service + "?lazy=true");
+ URL SERVICEURL3 = URL.valueOf("dubbo://127.0.0.1:9093/" + service + "?lazy=true");
+ URL SERVICEURL_DUBBO_NOPATH = URL.valueOf("dubbo://127.0.0.1:9092" + "?lazy=true");
+
+ @Before
+ public void setUp() {
+ }
+
+ private RegistryDirectory getRegistryDirectory(URL url) {
+ RegistryDirectory registryDirectory = new RegistryDirectory(URL.class, url);
+ registryDirectory.setProtocol(protocol);
+ // asert empty
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(0, invokers.size());
+ Assert.assertEquals(false, registryDirectory.isAvailable());
+ return registryDirectory;
+ }
+
+ private RegistryDirectory getRegistryDirectory() {
+ return getRegistryDirectory(noMeaningUrl);
+ }
+
+ @Test
+ public void test_Constructor_WithErrorParam() {
+ try {
+ new RegistryDirectory(null, null);
+ fail();
+ } catch (IllegalArgumentException e) {
+
+ }
+ try {
+ // null url
+ new RegistryDirectory(null, noMeaningUrl);
+ fail();
+ } catch (IllegalArgumentException e) {
+
+ }
+ try {
+ // no servicekey
+ new RegistryDirectory(RegistryDirectoryTest.class, URL.valueOf("dubbo://10.20.30.40:9090"));
+ fail();
+ } catch (IllegalArgumentException e) {
+
+ }
+ }
+
+ @Test
+ public void test_Constructor_CheckStatus() throws Exception {
+ URL url = URL.valueOf("notsupported://10.20.30.40/" + service + "?a=b").addParameterAndEncoded(Constants.REFER_KEY,
+ "foo=bar");
+ RegistryDirectory reg = getRegistryDirectory(url);
+ Field field = reg.getClass().getDeclaredField("queryMap");
+ field.setAccessible(true);
+ Map<String, String> queryMap = (Map<String, String>) field.get(reg);
+ Assert.assertEquals("bar", queryMap.get("foo"));
+ Assert.assertEquals(url.clearParameters().addParameter("foo", "bar"), reg.getUrl());
+ }
+
+ @Test
+ public void testNotified_Normal() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ test_Notified2invokers(registryDirectory);
+ test_Notified1invokers(registryDirectory);
+ test_Notified3invokers(registryDirectory);
+ testforbid(registryDirectory);
+ }
+
+ /**
+ * 测试推送只有router的情况
+ */
+ @Test
+ public void testNotified_Normal_withRouters() {
+ LogUtil.start();
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ test_Notified1invokers(registryDirectory);
+ test_Notified_only_routers(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>();
+ serviceUrls.add(new URL(Constants.EMPTY_PROTOCOL, Constants.ANYHOST_VALUE, 0, service, Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY));
+ 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"+"?refer=" + URL.encode("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_routers(RegistryDirectory registryDirectory) {
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(URL.valueOf("empty://127.0.0.1/?category=routers"));
+ registryDirectory.notify(serviceUrls);
+ }
+ // notify one invoker
+ private void test_Notified1invokers(RegistryDirectory registryDirectory) {
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));// .addParameter("refer.autodestroy", "true")
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ invocation = new RpcInvocation();
+
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+
+ invocation.setMethodName("getXXX");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+
+ invocation.setMethodName("getXXX1");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+
+ invocation.setMethodName("getXXX2");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ // 两个invoker===================================
+ private void test_Notified2invokers(RegistryDirectory registryDirectory) {
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ invocation = new RpcInvocation();
+
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ invocation.setMethodName("getXXX");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ invocation.setMethodName("getXXX1");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ invocation.setMethodName("getXXX2");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ // 通知成3个invoker===================================
+ private void test_Notified3invokers(RegistryDirectory registryDirectory) {
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+ serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1,getXXX2,getXXX3"));
+
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ invocation = new RpcInvocation();
+
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(3, invokers.size());
+
+ invocation.setMethodName("getXXX");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(3, invokers.size());
+
+ invocation.setMethodName("getXXX1");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(3, invokers.size());
+
+ invocation.setMethodName("getXXX2");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ invocation.setMethodName("getXXX3");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ @Test
+ public void testParametersMerge() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ URL regurl = noMeaningUrl.addParameter("test", "reg").addParameterAndEncoded(Constants.REFER_KEY,
+ "key=query&"
+ + Constants.LOADBALANCE_KEY
+ + "="
+ + LeastActiveLoadBalance.NAME);
+ RegistryDirectory<RegistryDirectoryTest> registryDirectory2 = new RegistryDirectory(
+ RegistryDirectoryTest.class,
+ regurl);
+ registryDirectory2.setProtocol(protocol);
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ // 检验注册中心的参数需要被清除
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ registryDirectory.notify(serviceUrls);
+
+ invocation = new RpcInvocation();
+ List invokers = registryDirectory.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals(null, url.getParameter("key"));
+ }
+ // 检验服务提供方的参数需要merge
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX2").addParameter("key", "provider"));
+
+ registryDirectory.notify(serviceUrls);
+ invocation = new RpcInvocation();
+ List invokers = registryDirectory.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals("provider", url.getParameter("key"));
+ }
+ // 检验服务query的参数需要与providermerge 。
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX3").addParameter("key", "provider"));
+
+ registryDirectory2.notify(serviceUrls);
+ invocation = new RpcInvocation();
+ List invokers = registryDirectory2.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals("query", url.getParameter("key"));
+ }
+
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ registryDirectory.notify(serviceUrls);
+
+ invocation = new RpcInvocation();
+ List invokers = registryDirectory.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals(false, url.getParameter(Constants.CHECK_KEY, false));
+ }
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter(Constants.LOADBALANCE_KEY, RoundRobinLoadBalance.NAME));
+ registryDirectory2.notify(serviceUrls);
+
+ invocation = new RpcInvocation();
+ invocation.setMethodName("get");
+ List invokers = registryDirectory2.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals(LeastActiveLoadBalance.NAME, url.getMethodParameter("get", Constants.LOADBALANCE_KEY));
+ }
+ //test geturl
+ {
+ Assert.assertEquals(null, registryDirectory2.getUrl().getParameter("mock"));
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter(Constants.MOCK_KEY, "true"));
+ registryDirectory2.notify(serviceUrls);
+
+ Assert.assertEquals("true", registryDirectory2.getUrl().getParameter("mock"));
+ }
+ }
+
+ /**
+ * When destroying, RegistryDirectory should: 1. be disconnected from Registry 2. destroy all invokers
+ */
+ @Test
+ public void testDestroy() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+ serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1,getXXX2,getXXX3"));
+
+ registryDirectory.notify(serviceUrls);
+ List<Invoker> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+ Assert.assertEquals(true, invokers.get(0).isAvailable());
+
+ registryDirectory.destroy();
+ Assert.assertEquals(false, registryDirectory.isAvailable());
+ Assert.assertEquals(false, invokers.get(0).isAvailable());
+ registryDirectory.destroy();
+
+ Map<String, List<Invoker<RegistryDirectoryTest>>> methodInvokerMap = registryDirectory.getMethodInvokerMap();
+ Map<String, Invoker<RegistryDirectoryTest>> urlInvokerMap = registryDirectory.getUrlInvokerMap();
+
+ Assert.assertTrue(methodInvokerMap == null);
+ Assert.assertEquals(0, urlInvokerMap.size());
+ // List<U> urls = mockRegistry.getSubscribedUrls();
+
+ RpcInvocation inv = new RpcInvocation();
+ try {
+ registryDirectory.list(inv);
+ fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("already destroyed"));
+ }
+ }
+
+ @Test
+ public void testDestroy_WithDestroyRegistry() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ CountDownLatch latch = new CountDownLatch(1);
+ registryDirectory.setRegistry(new MockRegistry(latch));
+ registryDirectory.subscribe(URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/DemoService?category=providers"));
+ 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).addParameters("check", "false", "interface", DemoService.class.getName()), invokers.get(0).getUrl());
+
+ }
+
+ enum Param {
+ MORGAN,
+ };
+
+ /**
+ * When the first arg of a method is String or Enum, Registry server can do parameter-value-based routing.
+ */
+ @Test
+ public void testParmeterRoute() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1.napoli"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1.MORGAN,getXXX2"));
+ serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1.morgan,getXXX2,getXXX3"));
+
+ registryDirectory.notify(serviceUrls);
+
+ invocation = new RpcInvocation(
+ Constants.$INVOKE,
+ new Class[] { String.class, String[].class, Object[].class },
+ new Object[] { "getXXX1", new String[] { "Enum" }, new Object[] { Param.MORGAN } });
+
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ /**
+ * Empty notify cause forbidden, non-empty notify cancels forbidden state
+ */
+ @Test
+ public void testEmptyNotifyCauseForbidden() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ List invokers = null;
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ registryDirectory.notify(serviceUrls);
+
+ RpcInvocation inv = new RpcInvocation();
+ try {
+ invokers = registryDirectory.list(inv);
+ } catch (RpcException e) {
+ Assert.assertEquals(RpcException.FORBIDDEN_EXCEPTION, e.getCode());
+ Assert.assertEquals(false, registryDirectory.isAvailable());
+ }
+
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+ serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1,getXXX2,getXXX3"));
+
+ registryDirectory.notify(serviceUrls);
+ inv.setMethodName("getXXX2");
+ invokers = registryDirectory.list(inv);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+ Assert.assertEquals(2, invokers.size());
+ }
+
+ private static boolean isScriptUnsupported = new ScriptEngineManager().getEngineByName("javascript") == null;
+
+ /**
+ * 1. notify twice, the second time notified router rules should completely replace the former one. 2. notify with
+ * no router url, do nothing to current routers 3. notify with only one router url, with router=clean, clear all
+ * current routers
+ */
+ @Test
+ public void testNotifyRouterUrls() {
+ if (isScriptUnsupported) return;
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ URL routerurl = URL.valueOf(Constants.ROUTE_PROTOCOL + "://127.0.0.1:9096/");
+ URL routerurl2 = URL.valueOf(Constants.ROUTE_PROTOCOL + "://127.0.0.1:9097/");
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ // without ROUTER_KEY, the first router should not be created.
+ serviceUrls.add(routerurl.addParameter(Constants.CATEGORY_KEY, Constants.ROUTERS_CATEGORY).addParameter(Constants.TYPE_KEY, "javascript").addParameter(Constants.ROUTER_KEY,
+ "notsupported").addParameter(Constants.RULE_KEY,
+ "function test1(){}"));
+ serviceUrls.add(routerurl2.addParameter(Constants.CATEGORY_KEY, Constants.ROUTERS_CATEGORY).addParameter(Constants.TYPE_KEY, "javascript").addParameter(Constants.ROUTER_KEY,
+ ScriptRouterFactory.NAME).addParameter(Constants.RULE_KEY,
+ "function test1(){}"));
+
+ registryDirectory.notify(serviceUrls);
+ List<Router> routers = registryDirectory.getRouters();
+ //default invocation selector
+ Assert.assertEquals(1+1, routers.size());
+ Assert.assertEquals(ScriptRouter.class, routers.get(0).getClass());
+
+ registryDirectory.notify(new ArrayList<URL>());
+ routers = registryDirectory.getRouters();
+ Assert.assertEquals(1 + 1, routers.size());
+ Assert.assertEquals(ScriptRouter.class, routers.get(0).getClass());
+
+ serviceUrls.clear();
+ serviceUrls.add(routerurl.addParameter(Constants.ROUTER_KEY, Constants.ROUTER_TYPE_CLEAR));
+ registryDirectory.notify(serviceUrls);
+ routers = registryDirectory.getRouters();
+ Assert.assertEquals(0 + 1, routers.size());
+ }
+
+ /**
+ * 测试override规则是否优先
+ * 场景:先推送override,后推送invoker
+ */
+ @Test
+ public void testNotifyoverrideUrls_beforeInvoker(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ List<URL> overrideUrls = new ArrayList<URL>();
+ overrideUrls.add(URL.valueOf("override://0.0.0.0?timeout=1&connections=5"));
+ registryDirectory.notify(overrideUrls);
+ //注册中心初始只推送override,dirctory状态应该是false,因为没有invoker存在。
+ Assert.assertEquals(false, registryDirectory.isAvailable());
+
+ //在推送两个provider,directory状态恢复为true
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("timeout", "1000"));
+ serviceUrls.add(SERVICEURL2.addParameter("timeout", "1000").addParameter("connections", "10"));
+
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ //开始验证参数值
+
+ invocation = new RpcInvocation();
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ Assert.assertEquals("override rute must be first priority", "1", invokers.get(0).getUrl().getParameter("timeout"));
+ Assert.assertEquals("override rute must be first priority", "5", invokers.get(0).getUrl().getParameter("connections"));
+ }
+
+ /**
+ * 测试override规则是否优先
+ * 场景:先推送override,后推送invoker
+ */
+ @Test
+ public void testNotifyoverrideUrls_afterInvoker(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+
+ //在推送两个provider,directory状态恢复为true
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("timeout", "1000"));
+ serviceUrls.add(SERVICEURL2.addParameter("timeout", "1000").addParameter("connections", "10"));
+
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ List<URL> overrideUrls = new ArrayList<URL>();
+ overrideUrls.add(URL.valueOf("override://0.0.0.0?timeout=1&connections=5"));
+ registryDirectory.notify(overrideUrls);
+
+ //开始验证参数值
+
+ invocation = new RpcInvocation();
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ Assert.assertEquals("override rute must be first priority", "1", invokers.get(0).getUrl().getParameter("timeout"));
+ Assert.assertEquals("override rute must be first priority", "5", invokers.get(0).getUrl().getParameter("connections"));
+ }
+
+ /**
+ * 测试override规则是否优先
+ * 场景:与invoker 一起推override规则
+ */
+ @Test
+ public void testNotifyoverrideUrls_withInvoker(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.addParameter("timeout", "1000"));
+ durls.add(SERVICEURL2.addParameter("timeout", "1000").addParameter("connections", "10"));
+ durls.add(URL.valueOf("override://0.0.0.0?timeout=1&connections=5"));
+
+ registryDirectory.notify(durls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ //开始验证参数值
+
+ invocation = new RpcInvocation();
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ Assert.assertEquals("override rute must be first priority", "1", invokers.get(0).getUrl().getParameter("timeout"));
+ Assert.assertEquals("override rute must be first priority", "5", invokers.get(0).getUrl().getParameter("connections"));
+ }
+
+ /**
+ * 测试override规则是否优先
+ * 场景:推送的规则与provider的参数是一样的
+ * 期望:不需要重新引用
+ */
+ @Test
+ public void testNotifyoverrideUrls_Nouse(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ invocation = new RpcInvocation();
+
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.addParameter("timeout", "1"));//一个一样,一个不一样
+ durls.add(SERVICEURL2.addParameter("timeout", "1").addParameter("connections", "5"));
+ registryDirectory.notify(durls);
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+ Invoker<?> a1Invoker = invokers.get(0);
+ Invoker<?> b1Invoker = invokers.get(1);
+
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://0.0.0.0?timeout=1&connections=5"));
+ registryDirectory.notify(durls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ Invoker<?> a2Invoker = invokers.get(0);
+ Invoker<?> b2Invoker = invokers.get(1);
+ //参数不一样,必须重新引用
+ Assert.assertFalse("object not same",a1Invoker == a2Invoker);
+
+ //参数一样,不能重新引用
+ Assert.assertTrue("object same",b1Invoker == b2Invoker);
+ }
+
+ /**
+ * 测试针对某个provider的Override规则
+ */
+ @Test
+ public void testNofityOverrideUrls_Provider(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ invocation = new RpcInvocation();
+
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.setHost("10.20.30.140").addParameter("timeout", "1"));//一个一样,一个不一样
+ durls.add(SERVICEURL2.setHost("10.20.30.141").addParameter("timeout", "2"));
+ registryDirectory.notify(durls);
+
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://0.0.0.0?timeout=3"));
+ durls.add(URL.valueOf("override://10.20.30.141:9092?timeout=4"));
+ registryDirectory.notify(durls);
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Invoker<?> aInvoker = invokers.get(0);
+ Invoker<?> bInvoker = invokers.get(1);
+ Assert.assertEquals("3",aInvoker.getUrl().getParameter("timeout"));
+ Assert.assertEquals("4",bInvoker.getUrl().getParameter("timeout"));
+ }
+
+ /**
+ * 测试清除override规则,同时下发清除规则和其他override规则
+ * 测试是否能够恢复到推送时的providerUrl
+ */
+ @Test
+ public void testNofityOverrideUrls_Clean1(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ invocation = new RpcInvocation();
+
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.setHost("10.20.30.140").addParameter("timeout", "1"));
+ registryDirectory.notify(durls);
+
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://0.0.0.0?timeout=1000"));
+ registryDirectory.notify(durls);
+
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://0.0.0.0?timeout=3"));
+ durls.add(URL.valueOf("override://0.0.0.0"));
+ registryDirectory.notify(durls);
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Invoker<?> aInvoker = invokers.get(0);
+ //需要恢复到最初的providerUrl
+ Assert.assertEquals("1",aInvoker.getUrl().getParameter("timeout"));
+ }
+
+ /**
+ * 测试清除override规则,只下发override清除规则
+ * 测试是否能够恢复到推送时的providerUrl
+ */
+ @Test
+ public void testNofityOverrideUrls_CleanOnly(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ invocation = new RpcInvocation();
+
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.setHost("10.20.30.140").addParameter("timeout", "1"));
+ registryDirectory.notify(durls);
+ Assert.assertEquals(null,registryDirectory.getUrl().getParameter("mock"));
+
+ //override
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://0.0.0.0?timeout=1000&mock=fail"));
+ registryDirectory.notify(durls);
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Invoker<?> aInvoker = invokers.get(0);
+ Assert.assertEquals("1000",aInvoker.getUrl().getParameter("timeout"));
+ Assert.assertEquals("fail",registryDirectory.getUrl().getParameter("mock"));
+
+ //override clean
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://0.0.0.0/dubbo.test.api.HelloService"));
+ registryDirectory.notify(durls);
+ invokers = registryDirectory.list(invocation);
+ aInvoker = invokers.get(0);
+ //需要恢复到最初的providerUrl
+ Assert.assertEquals("1",aInvoker.getUrl().getParameter("timeout"));
+
+ Assert.assertEquals(null,registryDirectory.getUrl().getParameter("mock"));
+ }
+
+ /**
+ * 测试同时推送清除override和针对某个provider的override
+ * 看override是否能够生效
+ */
+ @Test
+ public void testNofityOverrideUrls_CleanNOverride(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ invocation = new RpcInvocation();
+
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.setHost("10.20.30.140").addParameter("timeout", "1"));
+ registryDirectory.notify(durls);
+
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://0.0.0.0?timeout=3"));
+ durls.add(URL.valueOf("override://0.0.0.0"));
+ durls.add(URL.valueOf("override://10.20.30.140:9091?timeout=4"));
+ registryDirectory.notify(durls);
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Invoker<?> aInvoker = invokers.get(0);
+ Assert.assertEquals("4",aInvoker.getUrl().getParameter("timeout"));
+ }
+
+ /**
+ * 测试override通过enable=false,禁用所有服务提供者
+ * 预期:不能通过override禁用所有服务提供者.
+ */
+ @Test
+ public void testNofityOverrideUrls_disabled_allProvider(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ invocation = new RpcInvocation();
+
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.setHost("10.20.30.140"));
+ durls.add(SERVICEURL.setHost("10.20.30.141"));
+ registryDirectory.notify(durls);
+
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://0.0.0.0?"+Constants.ENABLED_KEY+"=false"));
+ registryDirectory.notify(durls);
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ //不能通过override禁用所有服务提供者.
+ Assert.assertEquals(2,invokers.size());
+ }
+
+ /**
+ * 测试override通过enable=false,禁用指定服务提供者
+ * 预期:可以禁用指定的服务提供者。
+ */
+ @Test
+ public void testNofityOverrideUrls_disabled_specifiedProvider(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ invocation = new RpcInvocation();
+
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.setHost("10.20.30.140"));
+ durls.add(SERVICEURL.setHost("10.20.30.141"));
+ registryDirectory.notify(durls);
+
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://10.20.30.140?"+Constants.DISABLED_KEY+"=true"));
+ registryDirectory.notify(durls);
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1,invokers.size());
+ Assert.assertEquals("10.20.30.141", invokers.get(0).getUrl().getHost());
+
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("empty://0.0.0.0?"+Constants.DISABLED_KEY+"=true&"+Constants.CATEGORY_KEY+"="+Constants.CONFIGURATORS_CATEGORY));
+ registryDirectory.notify(durls);
+ List<Invoker<?>> invokers2 = registryDirectory.list(invocation);
+ Assert.assertEquals(2,invokers2.size());
+ }
+
+ /**
+ * 测试override通过enable=false,禁用指定服务提供者
+ * 预期:可以禁用指定的服务提供者。
+ */
+ @Test
+ public void testNofity_To_Decrease_provider(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ invocation = new RpcInvocation();
+
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.setHost("10.20.30.140"));
+ durls.add(SERVICEURL.setHost("10.20.30.141"));
+ registryDirectory.notify(durls);
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2,invokers.size());
+
+ durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.setHost("10.20.30.140"));
+ registryDirectory.notify(durls);
+ List<Invoker<?>> invokers2 = registryDirectory.list(invocation);
+ Assert.assertEquals(1,invokers2.size());
+ Assert.assertEquals("10.20.30.140", invokers.get(0).getUrl().getHost());
+
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("empty://0.0.0.0?"+Constants.DISABLED_KEY+"=true&"+Constants.CATEGORY_KEY+"="+Constants.CONFIGURATORS_CATEGORY));
+ registryDirectory.notify(durls);
+ List<Invoker<?>> invokers3 = registryDirectory.list(invocation);
+ Assert.assertEquals(1,invokers3.size());
+ }
+
+ /**
+ * 测试override通过enable=false,禁用指定服务提供者
+ * 预期:可以禁用指定的服务提供者。
+ */
+ @Test
+ public void testNofity_disabled_specifiedProvider(){
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ invocation = new RpcInvocation();
+
+ // 初始就禁用
+ List<URL> durls = new ArrayList<URL>();
+ durls.add(SERVICEURL.setHost("10.20.30.140").addParameter(Constants.ENABLED_KEY, "false"));
+ durls.add(SERVICEURL.setHost("10.20.30.141"));
+ registryDirectory.notify(durls);
+
+ List<Invoker<?>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1,invokers.size());
+ Assert.assertEquals("10.20.30.141", invokers.get(0).getUrl().getHost());
+
+ // 通过覆盖规则启用
+ durls = new ArrayList<URL>();
+ durls.add(URL.valueOf("override://10.20.30.140?"+Constants.DISABLED_KEY+"=false"));
+ registryDirectory.notify(durls);
+ List<Invoker<?>> invokers2 = registryDirectory.list(invocation);
+ Assert.assertEquals(2,invokers2.size());
+ }
+
+ @Test
+ public void testNotifyRouterUrls_Clean() {
+ if (isScriptUnsupported) return;
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ URL routerurl = URL.valueOf(Constants.ROUTE_PROTOCOL + "://127.0.0.1:9096/").addParameter(Constants.ROUTER_KEY,
+ "javascript").addParameter(Constants.RULE_KEY,
+ "function test1(){}").addParameter(Constants.ROUTER_KEY,
+ "script"); // FIX
+ // BAD
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ // without ROUTER_KEY, the first router should not be created.
+ serviceUrls.add(routerurl);
+ registryDirectory.notify(serviceUrls);
+ List routers = registryDirectory.getRouters();
+ Assert.assertEquals(1 + 1, routers.size());
+
+ serviceUrls.clear();
+ serviceUrls.add(routerurl.addParameter(Constants.ROUTER_KEY, Constants.ROUTER_TYPE_CLEAR));
+ registryDirectory.notify(serviceUrls);
+ routers = registryDirectory.getRouters();
+ Assert.assertEquals(0 + 1, routers.size());
+ }
+
+ // mock protocol
+ /**
+ * 测试mock provider下发
+ */
+ @Test
+ public void testNotify_MockProviderOnly() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+ serviceUrls.add(SERVICEURL.setProtocol(Constants.MOCK_PROTOCOL));
+
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+ invocation = new RpcInvocation();
+
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ RpcInvocation mockinvocation = new RpcInvocation();
+ mockinvocation.setAttachment(Constants.INVOCATION_NEED_MOCK, "true");
+ invokers = registryDirectory.list(mockinvocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ //测试protocol匹配,只选择匹配的protocol进行refer
+ @Test
+ public void test_Notified_acceptProtocol0() {
+ URL errorPathUrl = URL.valueOf("notsupport:/xxx?refer=" + URL.encode("interface="+service));
+ RegistryDirectory registryDirectory = getRegistryDirectory(errorPathUrl);
+ List<URL> serviceUrls = new ArrayList<URL>();
+ URL dubbo1URL = URL.valueOf("dubbo://127.0.0.1:9098?lazy=true&methods=getXXX");
+ URL dubbo2URL = URL.valueOf("injvm://127.0.0.1:9099?lazy=true&methods=getXXX");
+ serviceUrls.add(dubbo1URL);
+ serviceUrls.add(dubbo2URL);
+ registryDirectory.notify(serviceUrls);
+
+ invocation = new RpcInvocation();
+
+ List<Invoker<DemoService>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+ }
+
+ //测试protocol匹配,只选择匹配的protocol进行refer
+ @Test
+ public void test_Notified_acceptProtocol1() {
+ URL errorPathUrl = URL.valueOf("notsupport:/xxx");
+ errorPathUrl = errorPathUrl.addParameterAndEncoded(Constants.REFER_KEY, "interface="+service + "&protocol=dubbo");
+ RegistryDirectory registryDirectory = getRegistryDirectory(errorPathUrl);
+ List<URL> serviceUrls = new ArrayList<URL>();
+ URL dubbo1URL = URL.valueOf("dubbo://127.0.0.1:9098?lazy=true&methods=getXXX");
+ URL dubbo2URL = URL.valueOf("injvm://127.0.0.1:9098?lazy=true&methods=getXXX");
+ serviceUrls.add(dubbo1URL);
+ serviceUrls.add(dubbo2URL);
+ registryDirectory.notify(serviceUrls);
+
+ invocation = new RpcInvocation();
+
+ List<Invoker<DemoService>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ //测试protocol匹配,只选择匹配的protocol进行refer
+ @Test
+ public void test_Notified_acceptProtocol2() {
+ URL errorPathUrl = URL.valueOf("notsupport:/xxx");
+ errorPathUrl = errorPathUrl.addParameterAndEncoded(Constants.REFER_KEY, "interface="+service + "&protocol=dubbo,injvm");
+ RegistryDirectory registryDirectory = getRegistryDirectory(errorPathUrl);
+ List<URL> serviceUrls = new ArrayList<URL>();
+ URL dubbo1URL = URL.valueOf("dubbo://127.0.0.1:9098?lazy=true&methods=getXXX");
+ URL dubbo2URL = URL.valueOf("injvm://127.0.0.1:9099?lazy=true&methods=getXXX");
+ serviceUrls.add(dubbo1URL);
+ serviceUrls.add(dubbo2URL);
+ registryDirectory.notify(serviceUrls);
+
+ invocation = new RpcInvocation();
+
+ List<Invoker<DemoService>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+ }
+
+ private static interface DemoService {
+ }
+
+ private static class MockRegistry implements Registry {
+
+ CountDownLatch latch;
+ boolean destroyWithError;
+
+ public MockRegistry(CountDownLatch latch){
+ this.latch = latch;
+ }
+
+ public MockRegistry(boolean destroyWithError){
+ this.destroyWithError = destroyWithError;
+ }
+
+ public void register(URL url) {
+
+ }
+
+ 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/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.java b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.java
new file mode 100644
index 0000000..17f330b
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.dubbo;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.registry.integration.RegistryProtocol;
+import com.alibaba.dubbo.registry.support.AbstractRegistry;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.cluster.support.FailfastCluster;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * RegistryProtocolTest
+ *
+ * @author tony.chenl
+ */
+public class RegistryProtocolTest {
+
+ final private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ static {
+ SimpleRegistryExporter.exportIfAbsent(9090);
+ }
+
+ final String service = "com.alibaba.dubbo.registry.protocol.DemoService:1.0.0";
+ final String serviceUrl = "dubbo://127.0.0.1:9453/" + service + "?notify=true&methods=test1,test2";
+ final URL registryUrl = URL.valueOf("registry://127.0.0.1:9090/");
+
+ @Test
+ public void testDefaultPort() {
+ RegistryProtocol registryProtocol = new RegistryProtocol();
+ assertEquals(9090, registryProtocol.getDefaultPort());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testExportUrlNull() {
+ RegistryProtocol registryProtocol = new RegistryProtocol();
+ registryProtocol.setCluster(new FailfastCluster());
+
+ Protocol dubboProtocol = DubboProtocol.getDubboProtocol();
+ registryProtocol.setProtocol(dubboProtocol);
+ Invoker<DemoService> invoker = new DubboInvoker<DemoService>(DemoService.class,
+ registryUrl, new ExchangeClient[] { new MockedClient("10.20.20.20", 2222, true) });
+ registryProtocol.export(invoker);
+ }
+
+ @Test
+ public void testExport() {
+ RegistryProtocol registryProtocol = new RegistryProtocol();
+ registryProtocol.setCluster(new FailfastCluster());
+ registryProtocol.setRegistryFactory(ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension());
+
+ Protocol dubboProtocol = DubboProtocol.getDubboProtocol();
+ registryProtocol.setProtocol(dubboProtocol);
+ URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl);
+ DubboInvoker<DemoService> invoker = new DubboInvoker<DemoService>(DemoService.class,
+ newRegistryUrl, new ExchangeClient[] { new MockedClient("10.20.20.20", 2222, true) });
+ Exporter<DemoService> exporter = registryProtocol.export(invoker);
+ Exporter<DemoService> exporter2 = registryProtocol.export(invoker);
+ //同一个invoker,多次export的exporter不同
+ Assert.assertNotSame(exporter, exporter2);
+ exporter.unexport();
+ exporter2.unexport();
+
+ }
+
+ @Test
+ public void testNotifyOverride() throws Exception{
+ URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl);
+ Invoker<RegistryProtocolTest> invoker = new MockInvoker<RegistryProtocolTest>(RegistryProtocolTest.class, newRegistryUrl);
+ Exporter<?> exporter = protocol.export(invoker);
+ RegistryProtocol rprotocol = RegistryProtocol.getRegistryProtocol();
+ NotifyListener listener = getListener(rprotocol);
+ List<URL> urls = new ArrayList<URL>();
+ urls.add(URL.valueOf("override://0.0.0.0/?timeout=1000"));
+ urls.add(URL.valueOf("override://0.0.0.0/"+ service + "?timeout=100"));
+ urls.add(URL.valueOf("override://0.0.0.0/"+ service + "?x=y"));
+ listener.notify(urls);
+
+ assertEquals(true, exporter.getInvoker().isAvailable());
+ assertEquals("100", exporter.getInvoker().getUrl().getParameter("timeout"));
+ assertEquals("y", exporter.getInvoker().getUrl().getParameter("x"));
+
+ exporter.unexport();
+ assertEquals(false, exporter.getInvoker().isAvailable());
+ destroyRegistryProtocol();
+
+ }
+
+
+ /**
+ * 服务名称不匹配,不能override invoker
+ * 服务名称匹配,服务版本号不匹配
+ */
+ @Test
+ public void testNotifyOverride_notmatch() throws Exception{
+ URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl);
+ Invoker<RegistryProtocolTest> invoker = new MockInvoker<RegistryProtocolTest>(RegistryProtocolTest.class, newRegistryUrl);
+ Exporter<?> exporter = protocol.export(invoker);
+ RegistryProtocol rprotocol = RegistryProtocol.getRegistryProtocol();
+ NotifyListener listener = getListener(rprotocol);
+ List<URL> urls = new ArrayList<URL>();
+ urls.add(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.registry.protocol.HackService?timeout=100"));
+ listener.notify(urls);
+ assertEquals(true, exporter.getInvoker().isAvailable());
+ assertEquals(null, exporter.getInvoker().getUrl().getParameter("timeout"));
+ exporter.unexport();
+ destroyRegistryProtocol();
+ }
+
+ /**
+ *测试destory registry ,exporter是否能够正常被destroy掉
+ */
+ @Test
+ public void testDestoryRegistry(){
+ URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl);
+ Invoker<RegistryProtocolTest> invoker = new MockInvoker<RegistryProtocolTest>(RegistryProtocolTest.class, newRegistryUrl);
+ Exporter<?> exporter = protocol.export(invoker);
+ destroyRegistryProtocol();
+ assertEquals(false, exporter.getInvoker().isAvailable());
+
+ }
+
+ private void destroyRegistryProtocol(){
+ Protocol registry = RegistryProtocol.getRegistryProtocol();
+ registry.destroy();
+ }
+
+ private NotifyListener getListener(RegistryProtocol protocol) throws Exception {
+ Field field = RegistryProtocol.class.getDeclaredField("listener");
+ field.setAccessible(true);
+ NotifyListener listener = (NotifyListener) field.get(protocol);
+ return listener;
+ }
+
+ static class MockInvoker<T> extends AbstractInvoker<T>{
+ public MockInvoker(Class<T> type, URL url) {
+ super(type, url);
+ }
+
+ @Override
+ protected Result doInvoke(Invocation invocation) throws Throwable {
+ //do nothing
+ return null;
+ }
+ }
+
+ static class MockRegistry extends AbstractRegistry{
+
+ public MockRegistry(URL url) {
+ super(url);
+ }
+
+ public boolean isAvailable() {
+ return true;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryStatusCheckerTest.java b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryStatusCheckerTest.java
new file mode 100644
index 0000000..b5e1c22
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryStatusCheckerTest.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.registry.dubbo;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.registry.status.RegistryStatusChecker;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * 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/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/SimpleRegistryExporter.java b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/SimpleRegistryExporter.java
new file mode 100644
index 0000000..4920396
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/SimpleRegistryExporter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.dubbo;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+/**
+ * SimpleRegistryExporter
+ *
+ * @author william.liangf
+ */
+public class SimpleRegistryExporter {
+
+ private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public synchronized static Exporter<RegistryService> exportIfAbsent(int port) {
+ try {
+ new ServerSocket(port).close();
+ return export(port);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public static Exporter<RegistryService> export(int port) {
+ return export(port, new SimpleRegistryService());
+ }
+
+ public static Exporter<RegistryService> export(int port, RegistryService registryService) {
+ return protocol.export(proxyFactory.getInvoker(registryService, RegistryService.class,
+ new URL("dubbo", NetUtils.getLocalHost(), port, RegistryService.class.getName())
+ .setPath(RegistryService.class.getName())
+ .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
+ .addParameter(Constants.CLUSTER_STICKY_KEY, "true")
+ .addParameter(Constants.CALLBACK_INSTANCES_LIMIT_KEY, "1000")
+ .addParameter("ondisconnect", "disconnect")
+ .addParameter("subscribe.1.callback", "true")
+ .addParameter("unsubscribe.1.callback", "false")));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/SimpleRegistryService.java b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/SimpleRegistryService.java
new file mode 100644
index 0000000..1438354
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/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.dubbo;
+
+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/dubbo-registry-default/src/test/resources/log4j.xml b/dubbo-registry/dubbo-registry-default/src/test/resources/log4j.xml
new file mode 100644
index 0000000..998d18c
--- /dev/null
+++ b/dubbo-registry/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/dubbo-registry-multicast/pom.xml b/dubbo-registry/dubbo-registry-multicast/pom.xml
new file mode 100644
index 0000000..8c65c83
--- /dev/null
+++ b/dubbo-registry/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-registry</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-registry-multicast</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
new file mode 100644
index 0000000..249547f
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.net.Socket;
+import java.util.ArrayList;
+import java.util.Arrays;
+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.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.common.utils.UrlUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.support.FailbackRegistry;
+
+/**
+ * MulticastRegistry
+ *
+ * @author william.liangf
+ */
+public class MulticastRegistry extends FailbackRegistry {
+
+ // 日志输出
+ private static final Logger logger = LoggerFactory.getLogger(MulticastRegistry.class);
+
+ private static final int DEFAULT_MULTICAST_PORT = 1234;
+
+ private final InetAddress mutilcastAddress;
+
+ private final MulticastSocket mutilcastSocket;
+
+ private final ConcurrentMap<URL, Set<URL>> received = new ConcurrentHashMap<URL, Set<URL>>();
+
+ private final ScheduledExecutorService cleanExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboMulticastRegistryCleanTimer", true));
+
+ private final ScheduledFuture<?> cleanFuture;
+
+ private final int cleanPeriod;
+
+ private volatile boolean admin = false;
+
+ public MulticastRegistry(URL url) {
+ super(url);
+ if (! isMulticastAddress(url.getHost())) {
+ throw new IllegalArgumentException("Invalid multicast address " + url.getHost() + ", scope: 224.0.0.0 - 239.255.255.255");
+ }
+ try {
+ mutilcastAddress = InetAddress.getByName(url.getHost());
+ mutilcastSocket = new MulticastSocket(url.getPort() == 0 ? DEFAULT_MULTICAST_PORT : url.getPort());
+ mutilcastSocket.setLoopbackMode(false);
+ mutilcastSocket.joinGroup(mutilcastAddress);
+ Thread thread = new Thread(new Runnable() {
+ public void run() {
+ byte[] buf = new byte[2048];
+ DatagramPacket recv = new DatagramPacket(buf, buf.length);
+ while (! mutilcastSocket.isClosed()) {
+ try {
+ mutilcastSocket.receive(recv);
+ String msg = new String(recv.getData()).trim();
+ int i = msg.indexOf('\n');
+ if (i > 0) {
+ msg = msg.substring(0, i).trim();
+ }
+ MulticastRegistry.this.receive(msg, (InetSocketAddress) recv.getSocketAddress());
+ Arrays.fill(buf, (byte)0);
+ } catch (Throwable e) {
+ if (! mutilcastSocket.isClosed()) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+ }
+ }, "DubboMulticastRegistryReceiver");
+ thread.setDaemon(true);
+ thread.start();
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ this.cleanPeriod = url.getParameter(Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT);
+ if (url.getParameter("clean", true)) {
+ this.cleanFuture = cleanExecutor.scheduleWithFixedDelay(new Runnable() {
+ public void run() {
+ try {
+ clean(); // 清除过期者
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected exception occur at clean expired provider, cause: " + t.getMessage(), t);
+ }
+ }
+ }, cleanPeriod, cleanPeriod, TimeUnit.MILLISECONDS);
+ } else {
+ this.cleanFuture = null;
+ }
+ }
+
+ 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 clean() {
+ if (admin) {
+ for (Set<URL> providers : new HashSet<Set<URL>>(received.values())) {
+ for (URL url : new HashSet<URL>(providers)) {
+ if (isExpired(url)) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Clean expired provider " + url);
+ }
+ doUnregister(url);
+ }
+ }
+ }
+ }
+ }
+
+ private boolean isExpired(URL url) {
+ if (Constants.CONSUMER_PROTOCOL.equals(url.getProtocol())
+ || Constants.ROUTE_PROTOCOL.equals(url.getProtocol())
+ || Constants.OVERRIDE_PROTOCOL.equals(url.getProtocol())) {
+ return false;
+ }
+ Socket socket = null;
+ try {
+ socket = new Socket(url.getHost(), url.getPort());
+ } catch (Throwable e) {
+ try {
+ Thread.sleep(100);
+ } catch (Throwable e2) {
+ }
+ Socket socket2 = null;
+ try {
+ socket2 = new Socket(url.getHost(), url.getPort());
+ } catch (Throwable e2) {
+ return true;
+ } finally {
+ if (socket2 != null) {
+ try {
+ socket2.close();
+ } catch (Throwable e2) {
+ }
+ }
+ }
+ } finally {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (Throwable e) {
+ }
+ }
+ }
+ return false;
+ }
+
+ private void receive(String msg, InetSocketAddress remoteAddress) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Receive multicast message: " + msg + " from " + remoteAddress);
+ }
+ if (msg.startsWith(Constants.REGISTER)) {
+ URL url = URL.valueOf(msg.substring(Constants.REGISTER.length()).trim());
+ registered(url);
+ } else if (msg.startsWith(Constants.UNREGISTER)) {
+ URL url = URL.valueOf(msg.substring(Constants.UNREGISTER.length()).trim());
+ unregistered(url);
+ } else if (msg.startsWith(Constants.SUBSCRIBE)) {
+ URL url = URL.valueOf(msg.substring(Constants.SUBSCRIBE.length()).trim());
+ Set<URL> urls = getRegistered();
+ if (urls != null && urls.size() > 0) {
+ for (URL u : urls) {
+ if (UrlUtils.isMatch(url, u)) {
+ String host = remoteAddress != null && remoteAddress.getAddress() != null
+ ? remoteAddress.getAddress().getHostAddress() : url.getIp();
+ if (url.getParameter("unicast", true) // 消费者的机器是否只有一个进程
+ && ! NetUtils.getLocalHost().equals(host)) { // 同机器多进程不能用unicast单播信息,否则只会有一个进程收到信息
+ unicast(Constants.REGISTER + " " + u.toFullString(), host);
+ } else {
+ broadcast(Constants.REGISTER + " " + u.toFullString());
+ }
+ }
+ }
+ }
+ }/* else if (msg.startsWith(UNSUBSCRIBE)) {
+ }*/
+ }
+
+ private void broadcast(String msg) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Send broadcast message: " + msg + " to " + mutilcastAddress + ":" + mutilcastSocket.getLocalPort());
+ }
+ try {
+ byte[] data = (msg + "\n").getBytes();
+ DatagramPacket hi = new DatagramPacket(data, data.length, mutilcastAddress, mutilcastSocket.getLocalPort());
+ mutilcastSocket.send(hi);
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ private void unicast(String msg, String host) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Send unicast message: " + msg + " to " + host + ":" + mutilcastSocket.getLocalPort());
+ }
+ try {
+ byte[] data = (msg + "\n").getBytes();
+ DatagramPacket hi = new DatagramPacket(data, data.length, InetAddress.getByName(host), mutilcastSocket.getLocalPort());
+ mutilcastSocket.send(hi);
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ protected void doRegister(URL url) {
+ broadcast(Constants.REGISTER + " " + url.toFullString());
+ }
+
+ protected void doUnregister(URL url) {
+ broadcast(Constants.UNREGISTER + " " + url.toFullString());
+ }
+
+ protected void doSubscribe(URL url, NotifyListener listener) {
+ if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
+ admin = true;
+ }
+ broadcast(Constants.SUBSCRIBE + " " + url.toFullString());
+ synchronized (listener) {
+ try {
+ listener.wait(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ protected void doUnsubscribe(URL url, NotifyListener listener) {
+ if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
+ && url.getParameter(Constants.REGISTER_KEY, true)) {
+ unregister(url);
+ }
+ broadcast(Constants.UNSUBSCRIBE + " " + url.toFullString());
+ }
+
+ public boolean isAvailable() {
+ try {
+ return mutilcastSocket != null;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ public void destroy() {
+ super.destroy();
+ try {
+ if (cleanFuture != null) {
+ cleanFuture.cancel(true);
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ try {
+ mutilcastSocket.leaveGroup(mutilcastAddress);
+ mutilcastSocket.close();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+
+ protected void registered(URL url) {
+ for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
+ URL key = entry.getKey();
+ if (UrlUtils.isMatch(key, url)) {
+ Set<URL> urls = received.get(key);
+ if (urls == null) {
+ received.putIfAbsent(key, new ConcurrentHashSet<URL>());
+ urls = received.get(key);
+ }
+ urls.add(url);
+ List<URL> list = toList(urls);
+ for (NotifyListener listener : entry.getValue()) {
+ notify(key, listener, list);
+ synchronized (listener) {
+ listener.notify();
+ }
+ }
+ }
+ }
+ }
+
+ protected void unregistered(URL url) {
+ for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
+ URL key = entry.getKey();
+ if (UrlUtils.isMatch(key, url)) {
+ Set<URL> urls = received.get(key);
+ if (urls != null) {
+ urls.remove(url);
+ }
+ List<URL> list = toList(urls);
+ for (NotifyListener listener : entry.getValue()) {
+ notify(key, listener, list);
+ }
+ }
+ }
+ }
+
+ protected void subscribed(URL url, NotifyListener listener) {
+ List<URL> urls = lookup(url);
+ notify(url, listener, urls);
+ }
+
+ private List<URL> toList(Set<URL> urls) {
+ List<URL> list = new ArrayList<URL>();
+ if (urls != null && urls.size() > 0) {
+ for (URL url : urls) {
+ list.add(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);
+ received.remove(url);
+ }
+
+ public List<URL> lookup(URL url) {
+ List<URL> urls= new ArrayList<URL>();
+ Map<String, List<URL>> notifiedUrls = getNotified().get(url);
+ if (notifiedUrls != null && notifiedUrls.size() > 0) {
+ for (List<URL> values : notifiedUrls.values()) {
+ urls.addAll(values);
+ }
+ }
+ if (urls == null || urls.size() == 0) {
+ List<URL> cacheUrls = getCacheUrls(url);
+ if (cacheUrls != null && cacheUrls.size() > 0) {
+ urls.addAll(cacheUrls);
+ }
+ }
+ if (urls == null || urls.size() == 0) {
+ for (URL u: getRegistered()) {
+ if (UrlUtils.isMatch(url, u)) {
+ urls.add(u);
+ }
+ }
+ }
+ if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
+ for (URL u: getSubscribed().keySet()) {
+ if (UrlUtils.isMatch(url, u)) {
+ urls.add(u);
+ }
+ }
+ }
+ return urls;
+ }
+
+ public MulticastSocket getMutilcastSocket() {
+ return mutilcastSocket;
+ }
+
+ public Map<URL, Set<URL>> getReceived() {
+ return received;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.java
new file mode 100644
index 0000000..138c952
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.multicast;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * MulticastRegistryLocator
+ *
+ * @author william.liangf
+ */
+public class MulticastRegistryFactory extends AbstractRegistryFactory {
+
+ public Registry createRegistry(URL url) {
+ return new MulticastRegistry(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..7eb7b3f
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+multicast=com.alibaba.dubbo.registry.multicast.MulticastRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-multicast/src/test/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryTest.java b/dubbo-registry/dubbo-registry-multicast/src/test/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryTest.java
new file mode 100644
index 0000000..0c05f43
--- /dev/null
+++ b/dubbo-registry/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<URL> registered = null;
+ // clear first
+ registered = registry.getRegistered();
+
+ for (int i = 0; i < 2; i++) {
+ registry.register(serviceUrl);
+ registered = registry.getRegistered();
+ assertTrue(registered.contains(serviceUrl));
+ }
+ // 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<URL, Set<NotifyListener>> arg = registry.getSubscribed();
+ assertEquals(consumerUrl, 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/dubbo-registry-redis/pom.xml b/dubbo-registry/dubbo-registry-redis/pom.xml
new file mode 100644
index 0000000..110ef6d
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-redis/pom.xml
@@ -0,0 +1,43 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-registry-redis</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The redis registry module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>redis.clients</groupId>
+ <artifactId>jedis</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java b/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
new file mode 100644
index 0000000..4600454
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
@@ -0,0 +1,606 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.redis;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.commons.pool.impl.GenericObjectPool;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPubSub;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.UrlUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.support.FailbackRegistry;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * RedisRegistry
+ *
+ * @author william.liangf
+ */
+public class RedisRegistry extends FailbackRegistry {
+
+ private static final Logger logger = LoggerFactory.getLogger(RedisRegistry.class);
+
+ private static final int DEFAULT_REDIS_PORT = 6379;
+
+ private final static String DEFAULT_ROOT = "dubbo";
+
+ private final ScheduledExecutorService expireExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryExpireTimer", true));
+
+ private final ScheduledFuture<?> expireFuture;
+
+ private final String root;
+
+ private final Map<String, JedisPool> jedisPools = new ConcurrentHashMap<String, JedisPool>();
+
+ private final ConcurrentMap<String, Notifier> notifiers = new ConcurrentHashMap<String, Notifier>();
+
+ private final int reconnectPeriod;
+
+ private final int expirePeriod;
+
+ private volatile boolean admin = false;
+
+ public RedisRegistry(URL url) {
+ super(url);
+ GenericObjectPool.Config config = new GenericObjectPool.Config();
+ config.testOnBorrow = url.getParameter("test.on.borrow", true);
+ config.testOnReturn = url.getParameter("test.on.return", false);
+ config.testWhileIdle = url.getParameter("test.while.idle", false);
+ if (url.getParameter("max.idle", 0) > 0)
+ config.maxIdle = url.getParameter("max.idle", 0);
+ if (url.getParameter("min.idle", 0) > 0)
+ config.minIdle = url.getParameter("min.idle", 0);
+ if (url.getParameter("max.active", 0) > 0)
+ config.maxActive = url.getParameter("max.active", 0);
+ if (url.getParameter("max.wait", 0) > 0)
+ config.maxWait = url.getParameter("max.wait", 0);
+ if (url.getParameter("num.tests.per.eviction.run", 0) > 0)
+ config.numTestsPerEvictionRun = url.getParameter("num.tests.per.eviction.run", 0);
+ if (url.getParameter("time.between.eviction.runs.millis", 0) > 0)
+ config.timeBetweenEvictionRunsMillis = url.getParameter("time.between.eviction.runs.millis", 0);
+ if (url.getParameter("min.evictable.idle.time.millis", 0) > 0)
+ config.minEvictableIdleTimeMillis = url.getParameter("min.evictable.idle.time.millis", 0);
+
+ List<String> addresses = new ArrayList<String>();
+ addresses.add(url.getAddress());
+ String[] backups = url.getParameter(Constants.BACKUP_KEY, new String[0]);
+ if (backups != null && backups.length > 0) {
+ addresses.addAll(Arrays.asList(backups));
+ }
+ for (String address : addresses) {
+ int i = address.indexOf(':');
+ String host;
+ int port;
+ if (i > 0) {
+ host = address.substring(0, i);
+ port = Integer.parseInt(address.substring(i + 1));
+ } else {
+ host = address;
+ port = DEFAULT_REDIS_PORT;
+ }
+ this.jedisPools.put(address, new JedisPool(config, host, port,
+ url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT)));
+ }
+
+ this.reconnectPeriod = url.getParameter(Constants.REGISTRY_RECONNECT_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RECONNECT_PERIOD);
+ String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
+ if (! group.startsWith(Constants.PATH_SEPARATOR)) {
+ group = Constants.PATH_SEPARATOR + group;
+ }
+ if (! group.endsWith(Constants.PATH_SEPARATOR)) {
+ group = group + Constants.PATH_SEPARATOR;
+ }
+ this.root = group;
+
+ this.expirePeriod = url.getParameter(Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT);
+ this.expireFuture = expireExecutor.scheduleWithFixedDelay(new Runnable() {
+ public void run() {
+ try {
+ deferExpired(); // 延长过期时间
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected exception occur at defer expire time, cause: " + t.getMessage(), t);
+ }
+ }
+ }, expirePeriod / 2, expirePeriod / 2, TimeUnit.MILLISECONDS);
+ }
+
+ private void deferExpired() {
+ for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
+ JedisPool jedisPool = entry.getValue();
+ try {
+ Jedis jedis = jedisPool.getResource();
+ try {
+ for (URL url : new HashSet<URL>(getRegistered())) {
+ if (url.getParameter(Constants.DYNAMIC_KEY, true)) {
+ String key = toCategoryPath(url);
+ if (jedis.hset(key, url.toFullString(), String.valueOf(System.currentTimeMillis() + expirePeriod)) == 1) {
+ jedis.publish(key, Constants.REGISTER);
+ }
+ }
+ }
+ if (admin) {
+ clean(jedis);
+ }
+ } finally {
+ jedisPool.returnResource(jedis);
+ }
+ } catch (Throwable t) {
+ logger.warn("Failed to write provider heartbeat to redis registry. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
+ }
+ }
+ }
+
+ // 监控中心负责删除过期脏数据
+ private void clean(Jedis jedis) {
+ Set<String> keys = jedis.keys(root + Constants.ANY_VALUE);
+ if (keys != null && keys.size() > 0) {
+ for (String key : keys) {
+ Map<String, String> values = jedis.hgetAll(key);
+ if (values != null && values.size() > 0) {
+ boolean delete = false;
+ long now = System.currentTimeMillis();
+ for (Map.Entry<String, String> entry : values.entrySet()) {
+ URL url = URL.valueOf(entry.getKey());
+ if (url.getParameter(Constants.DYNAMIC_KEY, true)) {
+ long expire = Long.parseLong(entry.getValue());
+ if (expire < now) {
+ jedis.hdel(key, entry.getKey());
+ delete = true;
+ if (logger.isWarnEnabled()) {
+ logger.warn("Delete expired key: " + key + " -> value: " + entry.getKey() + ", expire: " + new Date(expire) + ", now: " + new Date(now));
+ }
+ }
+ }
+ }
+ if (delete) {
+ jedis.publish(key, Constants.UNREGISTER);
+ }
+ }
+ }
+ }
+ }
+
+ public boolean isAvailable() {
+ for (JedisPool jedisPool : jedisPools.values()) {
+ try {
+ Jedis jedis = jedisPool.getResource();
+ try {
+ if (! jedis.isConnected()) {
+ return false;
+ }
+ } finally {
+ jedisPool.returnResource(jedis);
+ }
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ try {
+ expireFuture.cancel(true);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ try {
+ for (Notifier notifier : notifiers.values()) {
+ notifier.shutdown();
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
+ JedisPool jedisPool = entry.getValue();
+ try {
+ jedisPool.destroy();
+ } catch (Throwable t) {
+ logger.warn("Failed to destroy the redis registry client. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
+ }
+ }
+ }
+
+ @Override
+ public void doRegister(URL url) {
+ String key = toCategoryPath(url);
+ String value = url.toFullString();
+ String expire = String.valueOf(System.currentTimeMillis() + expirePeriod);
+ boolean success = false;
+ RpcException exception = null;
+ for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
+ JedisPool jedisPool = entry.getValue();
+ try {
+ Jedis jedis = jedisPool.getResource();
+ try {
+ jedis.hset(key, value, expire);
+ jedis.publish(key, Constants.REGISTER);
+ success = true;
+ } finally {
+ jedisPool.returnResource(jedis);
+ }
+ } catch (Throwable t) {
+ exception = new RpcException("Failed to register service to redis registry. registry: " + entry.getKey() + ", service: " + url + ", cause: " + t.getMessage(), t);
+ }
+ }
+ if (exception != null) {
+ if (success) {
+ logger.warn(exception.getMessage(), exception);
+ } else {
+ throw exception;
+ }
+ }
+ }
+
+ @Override
+ public void doUnregister(URL url) {
+ String key = toCategoryPath(url);
+ String value = url.toFullString();
+ RpcException exception = null;
+ for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
+ JedisPool jedisPool = entry.getValue();
+ try {
+ Jedis jedis = jedisPool.getResource();
+ try {
+ jedis.hdel(key, value);
+ jedis.publish(key, Constants.UNREGISTER);
+ } finally {
+ jedisPool.returnResource(jedis);
+ }
+ } catch (Throwable t) {
+ exception = new RpcException("Failed to unregister service to redis registry. registry: " + entry.getKey() + ", service: " + url + ", cause: " + t.getMessage(), t);
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ }
+
+ @Override
+ public void doSubscribe(final URL url, final NotifyListener listener) {
+ String service = toServicePath(url);
+ Notifier notifier = notifiers.get(service);
+ if (notifier == null) {
+ Notifier newNotifier = new Notifier(service);
+ notifiers.putIfAbsent(service, newNotifier);
+ notifier = notifiers.get(service);
+ if (notifier == newNotifier) {
+ notifier.start();
+ }
+ }
+ boolean success = false;
+ RpcException exception = null;
+ for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
+ JedisPool jedisPool = entry.getValue();
+ try {
+ Jedis jedis = jedisPool.getResource();
+ try {
+ if (service.endsWith(Constants.ANY_VALUE)) {
+ admin = true;
+ Set<String> keys = jedis.keys(service);
+ if (keys != null && keys.size() > 0) {
+ Map<String, Set<String>> serviceKeys = new HashMap<String, Set<String>>();
+ for (String key : keys) {
+ String serviceKey = toServicePath(key);
+ Set<String> sk = serviceKeys.get(serviceKey);
+ if (sk == null) {
+ sk = new HashSet<String>();
+ serviceKeys.put(serviceKey, sk);
+ }
+ sk.add(key);
+ }
+ for (Set<String> sk : serviceKeys.values()) {
+ doNotify(jedis, sk, url, Arrays.asList(listener));
+ }
+ }
+ } else {
+ doNotify(jedis, jedis.keys(service + Constants.PATH_SEPARATOR + Constants.ANY_VALUE), url, Arrays.asList(listener));
+ }
+ success = true;
+ break; // 只需读一个服务器的数据
+ } finally {
+ jedisPool.returnResource(jedis);
+ }
+ } catch(Throwable t) { // 尝试下一个服务器
+ exception = new RpcException("Failed to subscribe service from redis registry. registry: " + entry.getKey() + ", service: " + url + ", cause: " + t.getMessage(), t);
+ }
+ }
+ if (exception != null) {
+ if (success) {
+ logger.warn(exception.getMessage(), exception);
+ } else {
+ throw exception;
+ }
+ }
+ }
+
+ @Override
+ public void doUnsubscribe(URL url, NotifyListener listener) {
+ }
+
+ private void doNotify(Jedis jedis, String key) {
+ for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, Set<NotifyListener>>(getSubscribed()).entrySet()) {
+ doNotify(jedis, Arrays.asList(key), entry.getKey(), new HashSet<NotifyListener>(entry.getValue()));
+ }
+ }
+
+ private void doNotify(Jedis jedis, Collection<String> keys, URL url, Collection<NotifyListener> listeners) {
+ if (keys == null || keys.size() == 0
+ || listeners == null || listeners.size() == 0) {
+ return;
+ }
+ long now = System.currentTimeMillis();
+ List<URL> result = new ArrayList<URL>();
+ List<String> categories = Arrays.asList(url.getParameter(Constants.CATEGORY_KEY, new String[0]));
+ String consumerService = url.getServiceInterface();
+ for (String key : keys) {
+ if (! Constants.ANY_VALUE.equals(consumerService)) {
+ String prvoiderService = toServiceName(key);
+ if (! prvoiderService.equals(consumerService)) {
+ continue;
+ }
+ }
+ String category = toCategoryName(key);
+ if (! categories.contains(Constants.ANY_VALUE) && ! categories.contains(category)) {
+ continue;
+ }
+ List<URL> urls = new ArrayList<URL>();
+ Map<String, String> values = jedis.hgetAll(key);
+ if (values != null && values.size() > 0) {
+ for (Map.Entry<String, String> entry : values.entrySet()) {
+ URL u = URL.valueOf(entry.getKey());
+ if (! u.getParameter(Constants.DYNAMIC_KEY, true)
+ || Long.parseLong(entry.getValue()) >= now) {
+ if (UrlUtils.isMatch(url, u)) {
+ urls.add(u);
+ }
+ }
+ }
+ }
+ if (urls.isEmpty()) {
+ urls.add(url.setProtocol(Constants.EMPTY_PROTOCOL)
+ .setAddress(Constants.ANYHOST_VALUE)
+ .setPath(toServiceName(key))
+ .addParameter(Constants.CATEGORY_KEY, category));
+ }
+ result.addAll(urls);
+ if (logger.isInfoEnabled()) {
+ logger.info("redis notify: " + key + " = " + urls);
+ }
+ }
+ if (result == null || result.size() == 0) {
+ return;
+ }
+ for (NotifyListener listener : listeners) {
+ notify(url, listener, result);
+ }
+ }
+
+ private String toServiceName(String categoryPath) {
+ String servicePath = toServicePath(categoryPath);
+ return servicePath.startsWith(root) ? servicePath.substring(root.length()) : servicePath;
+ }
+
+ private String toCategoryName(String categoryPath) {
+ int i = categoryPath.lastIndexOf(Constants.PATH_SEPARATOR);
+ return i > 0 ? categoryPath.substring(i + 1) : categoryPath;
+ }
+
+ private String toServicePath(String categoryPath) {
+ int i;
+ if (categoryPath.startsWith(root)) {
+ i = categoryPath.indexOf(Constants.PATH_SEPARATOR, root.length());
+ } else {
+ i = categoryPath.indexOf(Constants.PATH_SEPARATOR);
+ }
+ return i > 0 ? categoryPath.substring(0, i) : categoryPath;
+ }
+
+ private String toServicePath(URL url) {
+ return root + url.getServiceInterface();
+ }
+
+ private String toCategoryPath(URL url) {
+ return toServicePath(url) + Constants.PATH_SEPARATOR + url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
+ }
+
+ private class NotifySub extends JedisPubSub {
+
+ private final JedisPool jedisPool;
+
+ public NotifySub(JedisPool jedisPool) {
+ this.jedisPool = jedisPool;
+ }
+
+ @Override
+ public void onMessage(String key, String msg) {
+ if (logger.isInfoEnabled()) {
+ logger.info("redis event: " + key + " = " + msg);
+ }
+ if (msg.equals(Constants.REGISTER)
+ || msg.equals(Constants.UNREGISTER)) {
+ try {
+ Jedis jedis = jedisPool.getResource();
+ try {
+ doNotify(jedis, key);
+ } finally {
+ jedisPool.returnResource(jedis);
+ }
+ } catch (Throwable t) { // TODO 通知失败没有恢复机制保障
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+
+ @Override
+ public void onPMessage(String pattern, String key, String msg) {
+ onMessage(key, msg);
+ }
+
+ @Override
+ public void onSubscribe(String key, int num) {
+ }
+
+ @Override
+ public void onPSubscribe(String pattern, int num) {
+ }
+
+ @Override
+ public void onUnsubscribe(String key, int num) {
+ }
+
+ @Override
+ public void onPUnsubscribe(String pattern, int num) {
+ }
+
+ }
+
+ private class Notifier extends Thread {
+
+ private final String service;
+
+ private volatile Jedis jedis;
+
+ private volatile boolean first = true;
+
+ private volatile boolean running = true;
+
+ private final AtomicInteger connectSkip = new AtomicInteger();
+
+ private final AtomicInteger connectSkiped = new AtomicInteger();
+
+ private final Random random = new Random();
+
+ private volatile int connectRandom;
+
+ private void resetSkip() {
+ connectSkip.set(0);
+ connectSkiped.set(0);
+ connectRandom = 0;
+ }
+
+ private boolean isSkip() {
+ int skip = connectSkip.get(); // 跳过次数增长
+ if (skip >= 10) { // 如果跳过次数增长超过10,取随机数
+ if (connectRandom == 0) {
+ connectRandom = random.nextInt(10);
+ }
+ skip = 10 + connectRandom;
+ }
+ if (connectSkiped.getAndIncrement() < skip) { // 检查跳过次数
+ return true;
+ }
+ connectSkip.incrementAndGet();
+ connectSkiped.set(0);
+ connectRandom = 0;
+ return false;
+ }
+
+ public Notifier(String service) {
+ super.setDaemon(true);
+ super.setName("DubboRedisSubscribe");
+ this.service = service;
+ }
+
+ @Override
+ public void run() {
+ while (running) {
+ try {
+ if (! isSkip()) {
+ try {
+ for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
+ JedisPool jedisPool = entry.getValue();
+ try {
+ jedis = jedisPool.getResource();
+ try {
+ if (service.endsWith(Constants.ANY_VALUE)) {
+ if (! first) {
+ first = false;
+ Set<String> keys = jedis.keys(service);
+ if (keys != null && keys.size() > 0) {
+ for (String s : keys) {
+ doNotify(jedis, s);
+ }
+ }
+ resetSkip();
+ }
+ jedis.psubscribe(new NotifySub(jedisPool), service); // 阻塞
+ } else {
+ if (! first) {
+ first = false;
+ doNotify(jedis, service);
+ resetSkip();
+ }
+ jedis.psubscribe(new NotifySub(jedisPool), service + Constants.PATH_SEPARATOR + Constants.ANY_VALUE); // 阻塞
+ }
+ break;
+ } finally {
+ jedisPool.returnBrokenResource(jedis);
+ }
+ } catch (Throwable t) { // 重试另一台
+ logger.warn("Failed to subscribe service from redis registry. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ sleep(reconnectPeriod);
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+
+ public void shutdown() {
+ try {
+ running = false;
+ jedis.disconnect();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+
+ }
+
+}
diff --git a/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistryFactory.java b/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistryFactory.java
new file mode 100644
index 0000000..52825c0
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistryFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.redis;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * RedisRegistryFactory
+ *
+ * @author william.liangf
+ */
+public class RedisRegistryFactory implements RegistryFactory {
+
+ public Registry getRegistry(URL url) {
+ return new RedisRegistry(url);
+ }
+
+}
diff --git a/dubbo-registry/dubbo-registry-redis/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry/dubbo-registry-redis/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..5ff2352
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-redis/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+redis=com.alibaba.dubbo.registry.redis.RedisRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-redis/src/test/java/com/alibaba/dubbo/registry/redis/RedisRegistryTest.java b/dubbo-registry/dubbo-registry-redis/src/test/java/com/alibaba/dubbo/registry/redis/RedisRegistryTest.java
new file mode 100644
index 0000000..7b710da
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-redis/src/test/java/com/alibaba/dubbo/registry/redis/RedisRegistryTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.redis;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.NotifyListener;
+
+/**
+ * RedisRegistryTest
+ *
+ * @author tony.chenl
+ */
+public class RedisRegistryTest {
+
+ String service = "com.alibaba.dubbo.test.injvmServie";
+ URL registryUrl = URL.valueOf("redis://239.255.255.255/");
+ URL serviceUrl = URL.valueOf("redis://redis/" + service
+ + "?notify=false&methods=test1,test2");
+ URL consumerUrl = URL.valueOf("redis://consumer/" + service + "?notify=false&methods=test1,test2");
+ // RedisRegistry registry = new RedisRegistry(registryUrl);
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ //registry.register(service, serviceUrl);
+ }
+
+ /**
+ * Test method for {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#register(java.util.Map)}.
+ */
+ @Test
+ public void testRegister() {
+ /*List<URL> registered = null;
+ // clear first
+ registered = registry.getRegistered(service);
+
+ for (int i = 0; i < 2; i++) {
+ registry.register(service, serviceUrl);
+ registered = registry.getRegistered(service);
+ assertTrue(registered.contains(serviceUrl));
+ }
+ // confirm only 1 regist success;
+ registered = registry.getRegistered(service);
+ assertEquals(1, registered.size());*/
+ }
+
+ /**
+ * Test method for
+ * {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#subscribe(java.util.Map, com.alibaba.dubbo.registry.support.NotifyListener)}
+ * .
+ */
+ @Test
+ public void testSubscribe() {
+ /*final String subscribearg = "arg1=1&arg2=2";
+ // verify lisener.
+ final AtomicReference<Map<String, String>> args = new AtomicReference<Map<String, String>>();
+ registry.subscribe(service, new URL("dubbo", NetUtils.getLocalHost(), 0, StringUtils.parseQueryString(subscribearg)), new NotifyListener() {
+
+ public void notify(List<URL> urls) {
+ // FIXME assertEquals(RedisRegistry.this.service, service);
+ args.set(urls.get(0).getParameters());
+ }
+ });
+ assertEquals(serviceUrl.toParameterString(), StringUtils.toQueryString(args.get()));
+ Map<String, String> arg = registry.getSubscribed(service);
+ assertEquals(subscribearg, StringUtils.toQueryString(arg));*/
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-zookeeper/pom.xml b/dubbo-registry/dubbo-registry-zookeeper/pom.xml
new file mode 100644
index 0000000..9639887
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-zookeeper/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-registry</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-registry-zookeeper</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.zookeeper</groupId>
+ <artifactId>zookeeper</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.github.sgroschupf</groupId>
+ <artifactId>zkclient</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java
new file mode 100644
index 0000000..7b24072
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.I0Itec.zkclient.IZkChildListener;
+import org.I0Itec.zkclient.IZkStateListener;
+import org.I0Itec.zkclient.ZkClient;
+import org.I0Itec.zkclient.exception.ZkNoNodeException;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.UrlUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.support.FailbackRegistry;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * ZookeeperRegistry
+ *
+ * @author william.liangf
+ */
+public class ZookeeperRegistry extends FailbackRegistry {
+
+ private final static Logger logger = LoggerFactory.getLogger(ZookeeperRegistry.class);
+
+ private final static int DEFAULT_ZOOKEEPER_PORT = 2181;
+
+ private final static String DEFAULT_ROOT = "dubbo";
+
+ private final String root;
+
+ // private final boolean auth;
+
+ // private final List<ACL> acl; // zkclient 0.1.0 unsupported
+
+ private final Set<String> anyServices = new ConcurrentHashSet<String>();
+
+ private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, IZkChildListener>> zkListeners = new ConcurrentHashMap<URL, ConcurrentMap<NotifyListener, IZkChildListener>>();
+
+ private final ZkClient zkClient;
+
+ private volatile KeeperState zkState = KeeperState.SyncConnected;
+
+ public ZookeeperRegistry(URL url) {
+ super(url);
+ // this.auth = url.getUsername() != null && url.getUsername().length() > 0
+ // && url.getPassword() != null && url.getPassword().length() > 0;
+ // this.acl = auth ? Ids.CREATOR_ALL_ACL : Ids.OPEN_ACL_UNSAFE;
+ String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
+ if (! group.startsWith(Constants.PATH_SEPARATOR)) {
+ group = Constants.PATH_SEPARATOR + group;
+ }
+ this.root = group;
+ 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));
+ }
+ }
+ zkClient = new ZkClient(address.toString());
+ zkClient.subscribeStateChanges(new IZkStateListener() {
+ public void handleStateChanged(KeeperState state) throws Exception {
+ ZookeeperRegistry.this.zkState = state;
+ }
+ public void handleNewSession() throws Exception {
+ recover();
+ }
+ });
+ }
+
+ public boolean isAvailable() {
+ return zkState == KeeperState.SyncConnected;
+ }
+
+ public void destroy() {
+ super.destroy();
+ try {
+ zkClient.close();
+ } catch (Exception e) {
+ logger.warn("Failed to close zookeeper client " + getUrl() + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ protected void doRegister(URL url) {
+ try {
+ if (url.getParameter(Constants.DYNAMIC_KEY, true)) {
+ zkClient.createPersistent(toCategoryPath(url), true);
+ zkClient.createEphemeral(toUrlPath(url));
+ } else {
+ zkClient.createPersistent(toUrlPath(url), true);
+ }
+ } catch (Throwable e) {
+ throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ protected void doUnregister(URL url) {
+ try {
+ zkClient.delete(toUrlPath(url));
+ } catch (Throwable e) {
+ throw new RpcException("Failed to unregister " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ protected void doSubscribe(final URL url, final NotifyListener listener) {
+ try {
+ if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
+ String root = toRootPath();
+ ConcurrentMap<NotifyListener, IZkChildListener> listeners = zkListeners.get(url);
+ if (listeners == null) {
+ zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, IZkChildListener>());
+ listeners = zkListeners.get(url);
+ }
+ IZkChildListener zkListener = listeners.get(listener);
+ if (zkListener == null) {
+ listeners.putIfAbsent(listener, new IZkChildListener() {
+ public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
+ for (String child : currentChilds) {
+ if (! anyServices.contains(child)) {
+ anyServices.add(child);
+ subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
+ Constants.CHECK_KEY, String.valueOf(false)), listener);
+ }
+ }
+ }
+ });
+ zkListener = listeners.get(listener);
+ }
+ List<String> services = zkClient.subscribeChildChanges(root, zkListener);
+ 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)), listener);
+ }
+ }
+ } else {
+ List<String> providers = new ArrayList<String>();
+ for (String path : toCategoriesPath(url)) {
+ ConcurrentMap<NotifyListener, IZkChildListener> listeners = zkListeners.get(url);
+ if (listeners == null) {
+ zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, IZkChildListener>());
+ listeners = zkListeners.get(url);
+ }
+ IZkChildListener zkListener = listeners.get(listener);
+ if (zkListener == null) {
+ listeners.putIfAbsent(listener, new IZkChildListener() {
+ public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
+ ZookeeperRegistry.this.notify(url, listener, toUrls(url, currentChilds));
+ }
+ });
+ zkListener = listeners.get(listener);
+ }
+ List<String> children = zkClient.subscribeChildChanges(path, zkListener);
+ if (children != null) {
+ providers.addAll(children);
+ }
+ }
+ List<URL> urls = toUrls(url, providers);
+ notify(url, listener, urls);
+ }
+ } catch (Throwable e) {
+ throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ protected void doUnsubscribe(URL url, NotifyListener listener) {
+ ConcurrentMap<NotifyListener, IZkChildListener> listeners = zkListeners.get(url);
+ if (listeners != null) {
+ IZkChildListener zkListener = listeners.get(listener);
+ if (zkListener != null) {
+ zkClient.unsubscribeChildChanges(toUrlPath(url), zkListener);
+ }
+ }
+ }
+
+ public List<URL> lookup(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("lookup url == null");
+ }
+ try {
+ List<String> providers = new ArrayList<String>();
+ for (String path : toCategoriesPath(url)) {
+ try {
+ List<String> children = zkClient.getChildren(path);
+ if (children != null) {
+ providers.addAll(children);
+ }
+ } catch (ZkNoNodeException e) {
+ // ignore
+ }
+ }
+ return toUrls(url, providers);
+ } catch (Throwable e) {
+ throw new RpcException("Failed to lookup " + url + " from zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ private String toRootDir() {
+ if (root.equals(Constants.PATH_SEPARATOR)) {
+ return root;
+ }
+ return root + Constants.PATH_SEPARATOR;
+ }
+
+ private String toRootPath() {
+ return root;
+ }
+
+ private String toServicePath(URL url) {
+ String name = url.getServiceInterface();
+ if (Constants.ANY_VALUE.equals(name)) {
+ return toRootPath();
+ }
+ return toRootDir() + URL.encode(name);
+ }
+
+ private String[] toCategoriesPath(URL url) {
+ String[] categroies;
+ if (Constants.ANY_VALUE.equals(url.getParameter(Constants.CATEGORY_KEY))) {
+ categroies = new String[] {Constants.PROVIDERS_CATEGORY, Constants.CONSUMERS_CATEGORY,
+ Constants.ROUTERS_CATEGORY, Constants.CONFIGURATORS_CATEGORY};
+ } else {
+ categroies = url.getParameter(Constants.CATEGORY_KEY, new String[] {Constants.DEFAULT_CATEGORY});
+ }
+ String[] paths = new String[categroies.length];
+ for (int i = 0; i < categroies.length; i ++) {
+ paths[i] = toServicePath(url) + Constants.PATH_SEPARATOR + categroies[i];
+ }
+ return paths;
+ }
+
+ private String toCategoryPath(URL url) {
+ return toServicePath(url) + Constants.PATH_SEPARATOR + url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
+ }
+
+ private String toUrlPath(URL url) {
+ return toCategoryPath(url) + Constants.PATH_SEPARATOR + URL.encode(url.toFullString());
+ }
+
+ private List<URL> toUrls(URL consumer, List<String> providers) throws KeeperException, InterruptedException {
+ List<URL> urls = new ArrayList<URL>();
+ if (providers != null && providers.size() > 0) {
+ for (String provider : providers) {
+ provider = URL.decode(provider);
+ if (provider.contains("://")) {
+ URL url = URL.valueOf(provider);
+ if (UrlUtils.isMatch(consumer, url)) {
+ urls.add(url);
+ }
+ }
+ }
+ }
+ if (urls != null && urls.isEmpty() && Constants.ANY_VALUE.equals(consumer.getServiceInterface())) {
+ urls.add(consumer.setProtocol(Constants.EMPTY_PROTOCOL));
+ }
+ return urls;
+ }
+
+ 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;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java
new file mode 100644
index 0000000..3151dde
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.zookeeper;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * ZookeeperRegistryFactory.
+ *
+ * @author william.liangf
+ */
+public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
+
+ public Registry createRegistry(URL url) {
+ return new ZookeeperRegistry(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..ced9ea0
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+zookeeper=com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryTest.java b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryTest.java
new file mode 100644
index 0000000..c430687
--- /dev/null
+++ b/dubbo-registry/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..9ce3d54
--- /dev/null
+++ b/dubbo-registry/pom.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.
+-->
+<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.2.2</version>
+ </parent>
+ <artifactId>dubbo-registry</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The registry module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-registry-api</module>
+ <module>dubbo-registry-default</module>
+ <module>dubbo-registry-multicast</module>
+ <module>dubbo-registry-zookeeper</module>
+ <module>dubbo-registry-redis</module>
+ </modules>
+</project>
diff --git a/dubbo-remoting/dubbo-remoting-api/pom.xml b/dubbo-remoting/dubbo-remoting-api/pom.xml
new file mode 100644
index 0000000..3c2c57b
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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-remoting</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-remoting-api</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Channel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Channel.java
new file mode 100644
index 0000000..8a4e4bb
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.java
new file mode 100644
index 0000000..ee663ac
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+
+/**
+ * ChannelHandler. (API, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.remoting.Transporter#bind(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @see com.alibaba.dubbo.remoting.Transporter#connect(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @author qian.lei
+ * @author william.liangf
+ */
+@SPI
+public interface ChannelHandler {
+
+ /**
+ * on channel connected.
+ *
+ * @param channel channel.
+ */
+ void connected(Channel channel) throws RemotingException;
+
+ /**
+ * on channel disconnected.
+ *
+ * @param channel channel.
+ */
+ void disconnected(Channel channel) throws RemotingException;
+
+ /**
+ * on message sent.
+ *
+ * @param channel channel.
+ * @param message message.
+ */
+ void sent(Channel channel, Object message) throws RemotingException;
+
+ /**
+ * on message received.
+ *
+ * @param channel channel.
+ * @param message message.
+ */
+ void received(Channel channel, Object message) throws RemotingException;
+
+ /**
+ * on exception caught.
+ *
+ * @param channel channel.
+ * @param exception exception.
+ */
+ void caught(Channel channel, Throwable exception) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Client.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Client.java
new file mode 100644
index 0000000..f547b8a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Codec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Codec.java
new file mode 100644
index 0000000..7145a93
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Codec.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * Codec. (SPI, Singleton, ThreadSafe)
+ *
+ * @author qianlei
+ * @author ding.lid
+ * @author william.liangf
+ */
+@SPI
+public interface Codec {
+
+ /**
+ * Need more input poison.
+ *
+ * @see #decode(Channel, InputStream)
+ */
+ Object NEED_MORE_INPUT = new Object();
+
+ /**
+ * Encode message.
+ *
+ * @param channel channel.
+ * @param output output stream.
+ * @param message message.
+ */
+ @Adaptive({Constants.CODEC_KEY})
+ void encode(Channel channel, OutputStream output, Object message) throws IOException;
+
+ /**
+ * Decode message.
+ *
+ * @see #NEED_MORE_INPUT
+ * @param channel channel.
+ * @param input input stream.
+ * @return message or <code>NEED_MORE_INPUT</code> poison.
+ */
+ @Adaptive({Constants.CODEC_KEY})
+ Object decode(Channel channel, InputStream input) throws IOException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Dispather.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Dispather.java
new file mode 100644
index 0000000..78fc371
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Dispather.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+import com.alibaba.dubbo.remoting.transport.dispather.all.AllDispather;
+
+/**
+ * ChannelHandlerWrapper (SPI, Singleton, ThreadSafe)
+ *
+ * @author chao.liuc
+ */
+@SPI(AllDispather.NAME)
+public interface Dispather {
+
+ /**
+ * dispath.
+ *
+ * @param handler
+ * @param url
+ * @return channel handler
+ */
+ @Adaptive({Constants.DISPATHER_KEY, Constants.CHANNEL_HANDLER_KEY})
+ ChannelHandler dispath(ChannelHandler handler, URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Endpoint.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Endpoint.java
new file mode 100644
index 0000000..267ad30
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/ExecutionException.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/ExecutionException.java
new file mode 100644
index 0000000..c54f6e4
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/RemotingException.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/RemotingException.java
new file mode 100644
index 0000000..7180b62
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Server.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Server.java
new file mode 100755
index 0000000..afe655a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/TimeoutException.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/TimeoutException.java
new file mode 100644
index 0000000..1b20999
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Transporter.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Transporter.java
new file mode 100644
index 0000000..f832615
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Transporter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import javax.sound.midi.Receiver;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * Transporter. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Transport_Layer">Transport Layer</a>
+ * <a href="http://en.wikipedia.org/wiki/Client%E2%80%93server_model">Client/Server</a>
+ *
+ * @see com.alibaba.dubbo.remoting.Transporters
+ * @author ding.lid
+ * @author william.liangf
+ */
+@SPI("netty")
+public interface Transporter {
+
+ /**
+ * Bind a server.
+ *
+ * @see com.alibaba.dubbo.remoting.Transporters#bind(URL, Receiver, ChannelHandler)
+ * @param url server url
+ * @param handler
+ * @return server
+ * @throws RemotingException
+ */
+ @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
+ Server bind(URL url, ChannelHandler handler) throws RemotingException;
+
+ /**
+ * Connect to a server.
+ *
+ * @see com.alibaba.dubbo.remoting.Transporters#connect(URL, Receiver, ChannelListener)
+ * @param url server url
+ * @param handler
+ * @return client
+ * @throws RemotingException
+ */
+ @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
+ Client connect(URL url, ChannelHandler handler) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Transporters.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Transporters.java
new file mode 100644
index 0000000..5f1d99c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/Transporters.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerDispatcher;
+
+/**
+ * Transporter facade. (API, Static, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class Transporters {
+
+ public static Server bind(String url, ChannelHandler... handler) throws RemotingException {
+ return bind(URL.valueOf(url), handler);
+ }
+
+ public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ if (handlers == null || handlers.length == 0) {
+ throw new IllegalArgumentException("handlers == null");
+ }
+ ChannelHandler handler;
+ if (handlers.length == 1) {
+ handler = handlers[0];
+ } else {
+ handler = new ChannelHandlerDispatcher(handlers);
+ }
+ return getTransporter().bind(url, handler);
+ }
+
+ public static Client connect(String url, ChannelHandler... handler) throws RemotingException {
+ return connect(URL.valueOf(url), handler);
+ }
+
+ public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ ChannelHandler handler;
+ if (handlers == null || handlers.length == 0) {
+ handler = new ChannelHandlerAdapter();
+ } else if (handlers.length == 1) {
+ handler = handlers[0];
+ } else {
+ handler = new ChannelHandlerDispatcher(handlers);
+ }
+ return getTransporter().connect(url, handler);
+ }
+
+ public static Transporter getTransporter() {
+ return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
+ }
+
+ static {
+ // check duplicate jar package
+ Version.checkDuplicate(Transporters.class);
+ Version.checkDuplicate(RemotingException.class);
+ }
+
+ private Transporters(){
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeChannel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeChannel.java
new file mode 100644
index 0000000..b62cb2c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeClient.java
new file mode 100644
index 0000000..6e0d61c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeHandler.java
new file mode 100644
index 0000000..b5e183b
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeServer.java
new file mode 100644
index 0000000..55ff1ba
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java
new file mode 100644
index 0000000..c303e42
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger;
+
+/**
+ * Exchanger. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Message_Exchange_Pattern">Message Exchange Pattern</a>
+ * <a href="http://en.wikipedia.org/wiki/Request-response">Request-Response</a>
+ *
+ * @author william.liangf
+ */
+@SPI(HeaderExchanger.NAME)
+public interface Exchanger {
+
+ /**
+ * bind.
+ *
+ * @param url
+ * @param handler
+ * @return message server
+ */
+ @Adaptive({Constants.EXCHANGER_KEY})
+ ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException;
+
+ /**
+ * connect.
+ *
+ * @param url
+ * @param handler
+ * @return message channel
+ */
+ @Adaptive({Constants.EXCHANGER_KEY})
+ ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchangers.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchangers.java
new file mode 100644
index 0000000..4f6c21a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchangers.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerDispatcher;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;
+
+/**
+ * Exchanger facade. (API, Static, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class Exchangers {
+
+ public static ExchangeServer bind(String url, Replier<?> replier) throws RemotingException {
+ return bind(URL.valueOf(url), replier);
+ }
+
+ public static ExchangeServer bind(URL url, Replier<?> replier) throws RemotingException {
+ return bind(url, new ChannelHandlerAdapter(), replier);
+ }
+
+ public static ExchangeServer bind(String url, ChannelHandler handler, Replier<?> replier) throws RemotingException {
+ return bind(URL.valueOf(url), handler, replier);
+ }
+
+ public static ExchangeServer bind(URL url, ChannelHandler handler, Replier<?> replier) throws RemotingException {
+ return bind(url, new ExchangeHandlerDispatcher(replier, handler));
+ }
+
+ public static ExchangeServer bind(String url, ExchangeHandler handler) throws RemotingException {
+ return bind(URL.valueOf(url), handler);
+ }
+
+ public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler == null");
+ }
+ url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
+ return getExchanger(url).bind(url, handler);
+ }
+
+ public static ExchangeClient connect(String url) throws RemotingException {
+ return connect(URL.valueOf(url));
+ }
+
+ public static ExchangeClient connect(URL url) throws RemotingException {
+ return connect(url, new ChannelHandlerAdapter(), null);
+ }
+
+ public static ExchangeClient connect(String url, Replier<?> replier) throws RemotingException {
+ return connect(URL.valueOf(url), new ChannelHandlerAdapter(), replier);
+ }
+
+ public static ExchangeClient connect(URL url, Replier<?> replier) throws RemotingException {
+ return connect(url, new ChannelHandlerAdapter(), replier);
+ }
+
+ public static ExchangeClient connect(String url, ChannelHandler handler, Replier<?> replier) throws RemotingException {
+ return connect(URL.valueOf(url), handler, replier);
+ }
+
+ public static ExchangeClient connect(URL url, ChannelHandler handler, Replier<?> replier) throws RemotingException {
+ return connect(url, new ExchangeHandlerDispatcher(replier, handler));
+ }
+
+ public static ExchangeClient connect(String url, ExchangeHandler handler) throws RemotingException {
+ return connect(URL.valueOf(url), handler);
+ }
+
+ public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler == null");
+ }
+ url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
+ return getExchanger(url).connect(url, handler);
+ }
+
+ public static Exchanger getExchanger(URL url) {
+ String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
+ return getExchanger(type);
+ }
+
+ public static Exchanger getExchanger(String type) {
+ return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
+ }
+
+ static {
+ // check duplicate jar package
+ Version.checkDuplicate(Exchangers.class);
+ }
+
+ private Exchangers(){
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Request.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Request.java
new file mode 100644
index 0000000..c490a5f
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Response.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/Response.java
new file mode 100644
index 0000000..0ac7070
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java
new file mode 100644
index 0000000..efe3d8b
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseFuture.java
new file mode 100644
index 0000000..69eb691
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java
new file mode 100644
index 0000000..181c34a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.codec;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.StreamUtils;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.exchange.support.DefaultFuture;
+import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec;
+
+/**
+ * ExchangeCodec.
+ *
+ * @author qianlei
+ * @author william.liangf
+ */
+public class ExchangeCodec extends TelnetCodec {
+
+ private static final Logger logger = LoggerFactory.getLogger(ExchangeCodec.class);
+
+ // header length.
+ protected static final int HEADER_LENGTH = 16;
+
+ // magic header.
+ protected static final short MAGIC = (short) 0xdabb;
+
+ protected static final byte MAGIC_HIGH = Bytes.short2bytes(MAGIC)[0];
+
+ protected static final byte MAGIC_LOW = Bytes.short2bytes(MAGIC)[1];
+
+ // message flag.
+ protected static final byte FLAG_REQUEST = (byte) 0x80;
+
+ protected static final byte FLAG_TWOWAY = (byte) 0x40;
+
+ protected static final byte FLAG_EVENT = (byte) 0x20;
+
+ protected static final int SERIALIZATION_MASK = 0x1f;
+
+ private static Map<Byte, Serialization> ID_SERIALIZATION_MAP = new HashMap<Byte, Serialization>();
+ static {
+ Set<String> supportedExtensions = ExtensionLoader.getExtensionLoader(Serialization.class).getSupportedExtensions();
+ for (String name : supportedExtensions) {
+ Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name);
+ byte idByte = serialization.getContentTypeId();
+ if (ID_SERIALIZATION_MAP.containsKey(idByte)) {
+ logger.error("Serialization extension " + serialization.getClass().getName()
+ + " has duplicate id to Serialization extension "
+ + ID_SERIALIZATION_MAP.get(idByte).getClass().getName()
+ + ", ignore this Serialization extension");
+ continue;
+ }
+ ID_SERIALIZATION_MAP.put(idByte, serialization);
+ }
+ }
+
+ public Short getMagicCode() {
+ return MAGIC;
+ }
+
+ public void encode(Channel channel, OutputStream os, Object msg) throws IOException {
+ if (msg instanceof Request) {
+ encodeRequest(channel, os, (Request) msg);
+ } else if (msg instanceof Response) {
+ encodeResponse(channel, os, (Response) msg);
+ } else {
+ super.encode(channel, os, msg);
+ }
+ }
+
+ public Object decode(Channel channel, InputStream is) throws IOException {
+ int readable = is.available();
+ byte[] header = new byte[Math.min(readable, HEADER_LENGTH)];
+ is.read(header);
+ return decode(channel, is, readable, header);
+ }
+
+ protected Object decode(Channel channel, InputStream is, int readable, byte[] header) throws IOException {
+ // check magic number.
+ if (readable > 0 && header[0] != MAGIC_HIGH
+ || readable > 1 && header[1] != MAGIC_LOW) {
+ int length = header.length;
+ if (header.length < readable) {
+ header = Bytes.copyOf(header, readable);
+ is.read(header, length, readable - length);
+ }
+ for (int i = 1; i < header.length - 1; i ++) {
+ if (header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) {
+ UnsafeByteArrayInputStream bis = ((UnsafeByteArrayInputStream) is);
+ bis.position(bis.position() - header.length + i);
+ header = Bytes.copyOf(header, i);
+ break;
+ }
+ }
+ return super.decode(channel, is, readable, header);
+ }
+ // check length.
+ if (readable < HEADER_LENGTH) {
+ return NEED_MORE_INPUT;
+ }
+
+ // get data length.
+ int len = Bytes.bytes2int(header, 12);
+ checkPayload(channel, len);
+
+ int tt = len + HEADER_LENGTH;
+ if( readable < tt ) {
+ return NEED_MORE_INPUT;
+ }
+
+ // limit input stream.
+ if( readable != tt )
+ is = StreamUtils.limitedInputStream(is, len);
+
+ byte flag = header[2], proto = (byte)( flag & SERIALIZATION_MASK );
+ Serialization s = getSerializationById(proto);
+ if (s == null) {
+ s = getSerialization(channel);
+ }
+ ObjectInput in = s.deserialize(channel.getUrl(), is);
+ // get request id.
+ long id = Bytes.bytes2long(header, 4);
+ if( ( flag & FLAG_REQUEST ) == 0 ) {
+ // decode response.
+ Response res = new Response(id);
+ if (( flag & FLAG_EVENT ) != 0){
+ res.setEvent(Response.HEARTBEAT_EVENT);
+ }
+ // get status.
+ byte status = header[3];
+ res.setStatus(status);
+ if( status == Response.OK ) {
+ try {
+ Object data;
+ if (res.isHeartbeat()) {
+ data = decodeHeartbeatData(channel, in);
+ } else if (res.isEvent()) {
+ data = decodeEventData(channel, in);
+ } else {
+ data = decodeResponseData(channel, in, getRequestData(id));
+ }
+ res.setResult(data);
+ } catch (Throwable t) {
+ res.setStatus(Response.CLIENT_ERROR);
+ res.setErrorMessage(StringUtils.toString(t));
+ }
+ } else {
+ res.setErrorMessage(in.readUTF());
+ }
+ return res;
+ } else {
+ // decode request.
+ Request req = new Request(id);
+ req.setVersion("2.0.0");
+ req.setTwoWay( ( flag & FLAG_TWOWAY ) != 0 );
+ if (( flag & FLAG_EVENT ) != 0 ){
+ req.setEvent(Request.HEARTBEAT_EVENT);
+ }
+ try {
+ Object data;
+ if (req.isHeartbeat()) {
+ data = decodeHeartbeatData(channel, in);
+ } else if (req.isEvent()) {
+ data = decodeEventData(channel, in);
+ } else {
+ data = decodeRequestData(channel, in);
+ }
+ req.setData(data);
+ } catch (Throwable t) {
+ // bad request
+ req.setBroken(true);
+ req.setData(t);
+ }
+ return req;
+ }
+ }
+
+ protected Object getRequestData(long id) {
+ DefaultFuture future = DefaultFuture.getFuture(id);
+ if (future == null)
+ return null;
+ Request req = future.getRequest();
+ if (req == null)
+ return null;
+ return req.getData();
+ }
+
+ protected void encodeRequest(Channel channel, OutputStream os, Request req) throws IOException {
+ Serialization serialization = getSerialization(channel);
+ // header.
+ byte[] header = new byte[HEADER_LENGTH];
+ // set magic number.
+ Bytes.short2bytes(MAGIC, header);
+
+ // set request and serialization flag.
+ header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
+
+ if (req.isTwoWay()) header[2] |= FLAG_TWOWAY;
+ if (req.isEvent()) header[2] |= FLAG_EVENT;
+
+ // set request id.
+ Bytes.long2bytes(req.getId(), header, 4);
+
+ // encode request data.
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
+ if (req.isEvent()) {
+ encodeEventData(channel, out, req.getData());
+ } else {
+ encodeRequestData(channel, out, req.getData());
+ }
+ out.flushBuffer();
+ bos.flush();
+ bos.close();
+ byte[] data = bos.toByteArray();
+ Bytes.int2bytes(data.length, header, 12);
+
+ // write
+ os.write(header); // write header.
+ os.write(data); // write data.
+ }
+
+ protected void encodeResponse(Channel channel, OutputStream os, Response res) throws IOException {
+ try {
+ Serialization serialization = getSerialization(channel);
+ // header.
+ byte[] header = new byte[HEADER_LENGTH];
+ // set magic number.
+ Bytes.short2bytes(MAGIC, header);
+ // set request and serialization flag.
+ header[2] = serialization.getContentTypeId();
+ if (res.isHeartbeat()) header[2] |= FLAG_EVENT;
+ // set response status.
+ byte status = res.getStatus();
+ header[3] = status;
+ // set request id.
+ Bytes.long2bytes(res.getId(), header, 4);
+
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
+ // encode response data or error message.
+ if (status == Response.OK) {
+ if (res.isHeartbeat()) {
+ encodeHeartbeatData(channel, out, res.getResult());
+ } else {
+ encodeResponseData(channel, out, res.getResult());
+ }
+ }
+ else out.writeUTF(res.getErrorMessage());
+ out.flushBuffer();
+ bos.flush();
+ bos.close();
+
+ byte[] data = bos.toByteArray();
+ Bytes.int2bytes(data.length, header, 12);
+ // write
+ os.write(header); // write header.
+ os.write(data); // write data.
+ } catch (Throwable t) {
+ // 发送失败信息给Consumer,否则Consumer只能等超时了
+ if (! res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) {
+ try {
+ // FIXME 在Codec中打印出错日志?在IoHanndler的caught中统一处理?
+ logger.warn("Fail to encode response: " + res + ", send bad_response info instead, cause: " + t.getMessage(), t);
+
+ Response r = new Response(res.getId(), res.getVersion());
+ r.setStatus(Response.BAD_RESPONSE);
+ r.setErrorMessage("Failed to send response: " + res + ", cause: " + StringUtils.toString(t));
+ channel.send(r);
+
+ return;
+ } catch (RemotingException e) {
+ logger.warn("Failed to send bad_response info back: " + res + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ // 重新抛出收到的异常
+ if (t instanceof IOException) {
+ throw (IOException) t;
+ } else if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else {
+ throw new RuntimeException(t.getMessage(), t);
+ }
+ }
+ }
+
+ private static final Serialization getSerializationById(Byte id) {
+ return ID_SERIALIZATION_MAP.get(id);
+ }
+
+ @Override
+ protected Object decodeData(ObjectInput in) throws IOException {
+ return decodeRequestData(in);
+ }
+
+ @Deprecated
+ protected Object decodeHeartbeatData(ObjectInput in) throws IOException {
+ try {
+ return in.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read object failed.", e));
+ }
+ }
+
+ protected Object decodeRequestData(ObjectInput in) throws IOException {
+ try {
+ return in.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read object failed.", e));
+ }
+ }
+
+ protected Object decodeResponseData(ObjectInput in) throws IOException {
+ try {
+ return in.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read object failed.", e));
+ }
+ }
+
+ @Override
+ protected void encodeData(ObjectOutput out, Object data) throws IOException {
+ encodeRequestData(out, data);
+ }
+
+ private void encodeEventData(ObjectOutput out, Object data) throws IOException {
+ out.writeObject(data);
+ }
+
+ @Deprecated
+ protected void encodeHeartbeatData(ObjectOutput out, Object data) throws IOException {
+ encodeEventData(out, data);
+ }
+
+ protected void encodeRequestData(ObjectOutput out, Object data) throws IOException {
+ out.writeObject(data);
+ }
+
+ protected void encodeResponseData(ObjectOutput out, Object data) throws IOException {
+ out.writeObject(data);
+ }
+
+ @Override
+ protected Object decodeData(Channel channel, ObjectInput in) throws IOException {
+ return decodeRequestData(channel ,in);
+ }
+
+ private Object decodeEventData(Channel channel, ObjectInput in) throws IOException {
+ try {
+ return in.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read object failed.", e));
+ }
+ }
+
+ @Deprecated
+ protected Object decodeHeartbeatData(Channel channel, ObjectInput in) throws IOException {
+ try {
+ return in.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read object failed.", e));
+ }
+ }
+
+ protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException {
+ return decodeRequestData(in);
+ }
+
+ protected Object decodeResponseData(Channel channel, ObjectInput in) throws IOException {
+ return decodeResponseData(in);
+ }
+
+ protected Object decodeResponseData(Channel channel, ObjectInput in, Object requestData) throws IOException {
+ return decodeResponseData(channel, in);
+ }
+
+ @Override
+ protected void encodeData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ encodeRequestData(channel, out, data);
+ }
+
+ private void encodeEventData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ encodeEventData(out, data);
+ }
+ @Deprecated
+ protected void encodeHeartbeatData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ encodeHeartbeatData(out, data);
+ }
+
+ protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ encodeRequestData(out, data);
+ }
+
+ protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ encodeResponseData(out, data);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/DefaultFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/DefaultFuture.java
new file mode 100644
index 0000000..d1bc6e4
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerAdapter.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerAdapter.java
new file mode 100644
index 0000000..378d7fb
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java
new file mode 100644
index 0000000..895b190
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeServerDelegate.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeServerDelegate.java
new file mode 100644
index 0000000..b15efcd
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/Replier.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/Replier.java
new file mode 100755
index 0000000..01a3c95
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ReplierDispatcher.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ReplierDispatcher.java
new file mode 100644
index 0000000..8a7ff7c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/SimpleFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/SimpleFuture.java
new file mode 100644
index 0000000..c09a0f0
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java
new file mode 100644
index 0000000..aa8341b
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
new file mode 100644
index 0000000..df90c7e
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support.header;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+
+/**
+ * DefaultMessageClient
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class HeaderExchangeClient implements ExchangeClient {
+
+ private static final Logger logger = LoggerFactory.getLogger( HeaderExchangeClient.class );
+
+ private static final ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(2, new NamedThreadFactory("dubbo-remoting-client-heartbeat", true));
+
+ // 心跳定时器
+ private ScheduledFuture<?> heatbeatTimer;
+
+ // 心跳超时,毫秒。缺省0,不会执行心跳。
+ private int heartbeat;
+
+ private int heartbeatTimeout;
+
+ private final Client client;
+
+ private final ExchangeChannel channel;
+
+ public HeaderExchangeClient(Client client){
+ if (client == null) {
+ throw new IllegalArgumentException("client == null");
+ }
+ this.client = client;
+ this.channel = new HeaderExchangeChannel(client);
+ this.heartbeat = client.getUrl().getParameter( Constants.HEARTBEAT_KEY, 0 );
+ this.heartbeatTimeout = client.getUrl().getParameter( Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3 );
+ if ( heartbeatTimeout < heartbeat * 2 ) {
+ throw new IllegalStateException( "heartbeatTimeout < heartbeatInterval * 2" );
+ }
+ startHeatbeatTimer();
+ }
+
+ public ResponseFuture request(Object request) throws RemotingException {
+ return channel.request(request);
+ }
+
+ public URL getUrl() {
+ return channel.getUrl();
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return channel.getRemoteAddress();
+ }
+
+ public ResponseFuture request(Object request, int timeout) throws RemotingException {
+ return channel.request(request, timeout);
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return channel.getChannelHandler();
+ }
+
+ public boolean isConnected() {
+ return channel.isConnected();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return channel.getLocalAddress();
+ }
+
+ public ExchangeHandler getExchangeHandler() {
+ return channel.getExchangeHandler();
+ }
+
+ public void send(Object message) throws RemotingException {
+ channel.send(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ channel.send(message, sent);
+ }
+
+ public boolean isClosed() {
+ return channel.isClosed();
+ }
+
+ public void close() {
+ doClose();
+ channel.close();
+ }
+
+ public void close(int timeout) {
+ doClose();
+ channel.close(timeout);
+ }
+
+ public void reset(URL url) {
+ client.reset(url);
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters){
+ reset(getUrl().addParameters(parameters.getParameters()));
+ }
+
+ public void reconnect() throws RemotingException {
+ client.reconnect();
+ }
+
+ public Object getAttribute(String key) {
+ return channel.getAttribute(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ channel.setAttribute(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ channel.removeAttribute(key);
+ }
+
+ public boolean hasAttribute(String key) {
+ return channel.hasAttribute(key);
+ }
+
+ private void startHeatbeatTimer() {
+ stopHeartbeatTimer();
+ if ( heartbeat > 0 ) {
+ heatbeatTimer = scheduled.scheduleWithFixedDelay(
+ new HeartBeatTask( new HeartBeatTask.ChannelProvider() {
+ public Collection<Channel> getChannels() {
+ return Collections.<Channel>singletonList( HeaderExchangeClient.this );
+ }
+ }, heartbeat, heartbeatTimeout),
+ heartbeat, heartbeat, TimeUnit.MILLISECONDS );
+ }
+ }
+
+ private void stopHeartbeatTimer() {
+ if (heatbeatTimer != null && ! heatbeatTimer.isCancelled()) {
+ try {
+ heatbeatTimer.cancel(true);
+ scheduled.purge();
+ } catch ( Throwable e ) {
+ if (logger.isWarnEnabled()) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ heatbeatTimer =null;
+ }
+
+ private void doClose() {
+ stopHeartbeatTimer();
+ }
+
+ @Override
+ public String toString() {
+ return "HeaderExchangeClient [channel=" + channel + "]";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java
new file mode 100644
index 0000000..6ab7167
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java
new file mode 100644
index 0000000..08a32fd
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support.header;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.support.DefaultFuture;
+
+/**
+ * ExchangeServerImpl
+ *
+ * @author william.liangf
+ */
+public class HeaderExchangeServer implements ExchangeServer {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1,
+ new NamedThreadFactory(
+ "dubbo-remoting-server-heartbeat",
+ true));
+
+ // 心跳定时器
+ private ScheduledFuture<?> heatbeatTimer;
+
+ // 心跳超时,毫秒。缺省0,不会执行心跳。
+ private int heartbeat;
+
+ private int heartbeatTimeout;
+
+ private final Server server;
+
+ private volatile boolean closed = false;
+
+ public HeaderExchangeServer(Server server) {
+ if (server == null) {
+ throw new IllegalArgumentException("server == null");
+ }
+ this.server = server;
+ this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
+ this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
+ if (heartbeatTimeout < heartbeat * 2) {
+ throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
+ }
+ startHeatbeatTimer();
+ }
+
+ public Server getServer() {
+ return server;
+ }
+
+ public boolean isClosed() {
+ return server.isClosed();
+ }
+
+ private boolean isRunning() {
+ Collection<Channel> channels = getChannels();
+ for (Channel channel : channels) {
+ if (DefaultFuture.hasFuture(channel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void close() {
+ doClose();
+ server.close();
+ }
+
+ public void close(final int timeout) {
+ if (timeout > 0) {
+ final long max = (long) timeout;
+ final long start = System.currentTimeMillis();
+ if (getUrl().getParameter(Constants.CHANNEL_SEND_READONLYEVENT_KEY, false)){
+ sendChannelReadOnlyEvent();
+ }
+ while (HeaderExchangeServer.this.isRunning()
+ && System.currentTimeMillis() - start < max) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ doClose();
+ server.close(timeout);
+ }
+
+ private void sendChannelReadOnlyEvent(){
+ Request request = new Request();
+ request.setEvent(Request.READONLY_EVENT);
+ request.setTwoWay(false);
+ request.setVersion(Version.getVersion());
+
+ Collection<Channel> channels = getChannels();
+ for (Channel channel : channels) {
+ try {
+ if (channel.isConnected())channel.send(request, getUrl().getParameter(Constants.CHANNEL_READONLYEVENT_SENT_KEY, true));
+ } catch (RemotingException e) {
+ logger.warn("send connot write messge error.", e);
+ }
+ }
+ }
+
+ private void doClose() {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ stopHeartbeatTimer();
+ try {
+ scheduled.shutdown();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+
+ public Collection<ExchangeChannel> getExchangeChannels() {
+ Collection<ExchangeChannel> exchangeChannels = new ArrayList<ExchangeChannel>();
+ Collection<Channel> channels = server.getChannels();
+ if (channels != null && channels.size() > 0) {
+ for (Channel channel : channels) {
+ exchangeChannels.add(HeaderExchangeChannel.getOrAddChannel(channel));
+ }
+ }
+ return exchangeChannels;
+ }
+
+ public ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress) {
+ Channel channel = server.getChannel(remoteAddress);
+ return HeaderExchangeChannel.getOrAddChannel(channel);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Collection<Channel> getChannels() {
+ return (Collection)getExchangeChannels();
+ }
+
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ return getExchangeChannel(remoteAddress);
+ }
+
+ public boolean isBound() {
+ return server.isBound();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return server.getLocalAddress();
+ }
+
+ public URL getUrl() {
+ return server.getUrl();
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return server.getChannelHandler();
+ }
+
+ public void reset(URL url) {
+ server.reset(url);
+ try {
+ if (url.hasParameter(Constants.HEARTBEAT_KEY)
+ || url.hasParameter(Constants.HEARTBEAT_TIMEOUT_KEY)) {
+ int h = url.getParameter(Constants.HEARTBEAT_KEY, heartbeat);
+ int t = url.getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, h * 3);
+ if (t < h * 2) {
+ throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
+ }
+ if (h != heartbeat || t != heartbeatTimeout) {
+ heartbeat = h;
+ heartbeatTimeout = t;
+ startHeatbeatTimer();
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters){
+ reset(getUrl().addParameters(parameters.getParameters()));
+ }
+
+ public void send(Object message) throws RemotingException {
+ if (closed) {
+ throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The server " + getLocalAddress() + " is closed!");
+ }
+ server.send(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ if (closed) {
+ throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The server " + getLocalAddress() + " is closed!");
+ }
+ server.send(message, sent);
+ }
+
+ private void startHeatbeatTimer() {
+ stopHeartbeatTimer();
+ if (heartbeat > 0) {
+ heatbeatTimer = scheduled.scheduleWithFixedDelay(
+ new HeartBeatTask( new HeartBeatTask.ChannelProvider() {
+ public Collection<Channel> getChannels() {
+ return Collections.unmodifiableCollection(
+ HeaderExchangeServer.this.getChannels() );
+ }
+ }, heartbeat, heartbeatTimeout),
+ heartbeat, heartbeat,TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private void stopHeartbeatTimer() {
+ try {
+ ScheduledFuture<?> timer = heatbeatTimer;
+ if (timer != null && ! timer.isCancelled()) {
+ timer.cancel(true);
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ } finally {
+ heatbeatTimer =null;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.java
new file mode 100644
index 0000000..76badec
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support.header;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Transporters;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchanger;
+
+/**
+ * DefaultMessenger
+ *
+ * @author william.liangf
+ */
+public class HeaderExchanger implements Exchanger {
+
+ public static final String NAME = "header";
+
+ public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
+ return new HeaderExchangeClient(Transporters.connect(url, new HeaderExchangeHandler(handler)));
+ }
+
+ public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
+ return new HeaderExchangeServer(Transporters.bind(url, new HeaderExchangeHandler(handler)));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTask.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTask.java
new file mode 100644
index 0000000..1f97370
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTask.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.remoting.exchange.support.header;
+
+import java.util.Collection;
+
+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.exchange.Request;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+final class HeartBeatTask implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger( HeartBeatTask.class );
+
+ private ChannelProvider channelProvider;
+
+ private int heartbeat;
+
+ private int heartbeatTimeout;
+
+ HeartBeatTask( ChannelProvider provider, int heartbeat, int heartbeatTimeout ) {
+ this.channelProvider = provider;
+ this.heartbeat = heartbeat;
+ this.heartbeatTimeout = heartbeatTimeout;
+ }
+
+ public void run() {
+ try {
+ long now = System.currentTimeMillis();
+ for ( Channel channel : channelProvider.getChannels() ) {
+ if (channel.isClosed()) {
+ continue;
+ }
+ try {
+ Long lastRead = ( Long ) channel.getAttribute(
+ HeaderExchangeHandler.KEY_READ_TIMESTAMP );
+ Long lastWrite = ( Long ) channel.getAttribute(
+ HeaderExchangeHandler.KEY_WRITE_TIMESTAMP );
+ if ( ( lastRead != null && now - lastRead > heartbeat )
+ || ( lastWrite != null && now - lastWrite > heartbeat ) ) {
+ Request req = new Request();
+ req.setVersion( "2.0.0" );
+ req.setTwoWay( true );
+ req.setEvent( Request.HEARTBEAT_EVENT );
+ channel.send( req );
+ if ( logger.isDebugEnabled() ) {
+ logger.debug( "Send heartbeat to remote channel "
+ + channel.getRemoteAddress() + "." );
+ }
+ }
+ if ( lastRead != null && now - lastRead > heartbeatTimeout ) {
+ logger.warn( "Close channel " + channel
+ + ", because heartbeat read idle time out." );
+ if (channel instanceof Client) {
+ try {
+ ((Client)channel).reconnect();
+ }catch (Exception e) {
+ //do nothing
+ }
+ } else {
+ channel.close();
+ }
+ }
+ } catch ( Throwable t ) {
+ logger.warn( "Exception when heartbeat to remote channel " + channel.getRemoteAddress(), t );
+ }
+ }
+ } catch ( Throwable t ) {
+ logger.info( "Exception when heartbeat to remote channel(s): ", t );
+ }
+ }
+
+ interface ChannelProvider {
+ Collection<Channel> getChannels();
+ }
+
+}
+
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java
new file mode 100644
index 0000000..da73d39
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet;
+
+import com.alibaba.dubbo.common.extension.SPI;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * TelnetHandler
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface TelnetHandler {
+
+ /**
+ * telnet.
+ *
+ * @param channel
+ * @param message
+ */
+ String telnet(Channel channel, String message) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java
new file mode 100644
index 0000000..9642a5a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.codec;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.codec.TransportCodec;
+
+/**
+ * TelnetCodec
+ *
+ * @author heyman
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class TelnetCodec extends TransportCodec {
+
+ private static final Logger logger = LoggerFactory.getLogger(TelnetCodec.class);
+
+ private static final String HISTORY_LIST_KEY = "telnet.history.list";
+
+ private static final String HISTORY_INDEX_KEY = "telnet.history.index";
+
+ private static final byte[] UP = new byte[] {27, 91, 65};
+
+ private static final byte[] DOWN = new byte[] {27, 91, 66};
+
+ private static final List<?> ENTER = Arrays.asList(new Object[] { new byte[] { '\r', '\n' } /* Windows Enter */, new byte[] { '\n' } /* Linux Enter */ });
+
+ private static final List<?> EXIT = Arrays.asList(new Object[] { new byte[] { 3 } /* Windows Ctrl+C */, new byte[] { -1, -12, -1, -3, 6 } /* Linux Ctrl+C */, new byte[] { -1, -19, -1, -3, 6 } /* Linux Pause */ });
+
+ public void encode(Channel channel, OutputStream output, Object message) throws IOException {
+ if (message instanceof String) {
+ if (isClientSide(channel)) {
+ message = message + "\r\n";
+ }
+ byte[] msgData = ((String) message).getBytes(getCharset(channel).name());
+ output.write(msgData);
+ output.flush();
+ } else {
+ super.encode(channel, output, message);
+ }
+ }
+
+ public Object decode(Channel channel, InputStream is) throws IOException {
+ int readable = is.available();
+ byte[] message = new byte[readable];
+ is.read(message);
+ return decode(channel, is, readable, message);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Object decode(Channel channel, InputStream is, int readable, byte[] message) throws IOException {
+ if (isClientSide(channel)) {
+ return toString(message, getCharset(channel));
+ }
+ checkPayload(channel, readable);
+ if (message == null || message.length == 0) {
+ return NEED_MORE_INPUT;
+ }
+
+ if (message[message.length - 1] == '\b') { // Windows backspace echo
+ try {
+ boolean doublechar = message.length >= 3 && message[message.length - 3] < 0; // double byte char
+ channel.send(new String(doublechar ? new byte[] {32, 32, 8, 8} : new byte[] {32, 8}, getCharset(channel).name()));
+ } catch (RemotingException e) {
+ throw new IOException(StringUtils.toString(e));
+ }
+ return NEED_MORE_INPUT;
+ }
+
+ for (Object command : EXIT) {
+ if (isEquals(message, (byte[]) command)) {
+ if (logger.isInfoEnabled()) {
+ logger.info(new Exception("Close channel " + channel + " on exit command: " + Arrays.toString((byte[])command)));
+ }
+ channel.close();
+ return null;
+ }
+ }
+
+ boolean up = endsWith(message, UP);
+ boolean down = endsWith(message, DOWN);
+ if (up || down) {
+ LinkedList<String> history = (LinkedList<String>) channel.getAttribute(HISTORY_LIST_KEY);
+ if (history == null || history.size() == 0) {
+ return NEED_MORE_INPUT;
+ }
+ Integer index = (Integer) channel.getAttribute(HISTORY_INDEX_KEY);
+ Integer old = index;
+ if (index == null) {
+ index = history.size() - 1;
+ } else {
+ if (up) {
+ index = index - 1;
+ if (index < 0) {
+ index = history.size() - 1;
+ }
+ } else {
+ index = index + 1;
+ if (index > history.size() - 1) {
+ index = 0;
+ }
+ }
+ }
+ if (old == null || ! old.equals(index)) {
+ channel.setAttribute(HISTORY_INDEX_KEY, index);
+ String value = history.get(index);
+ if (old != null && old >= 0 && old < history.size()) {
+ String ov = history.get(old);
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < ov.length(); i ++) {
+ buf.append("\b");
+ }
+ for (int i = 0; i < ov.length(); i ++) {
+ buf.append(" ");
+ }
+ for (int i = 0; i < ov.length(); i ++) {
+ buf.append("\b");
+ }
+ value = buf.toString() + value;
+ }
+ try {
+ channel.send(value);
+ } catch (RemotingException e) {
+ throw new IOException(StringUtils.toString(e));
+ }
+ }
+ return NEED_MORE_INPUT;
+ }
+ for (Object command : EXIT) {
+ if (isEquals(message, (byte[]) command)) {
+ if (logger.isInfoEnabled()) {
+ logger.info(new Exception("Close channel " + channel + " on exit command " + command));
+ }
+ channel.close();
+ return null;
+ }
+ }
+ byte[] enter = null;
+ for (Object command : ENTER) {
+ if (endsWith(message, (byte[]) command)) {
+ enter = (byte[]) command;
+ break;
+ }
+ }
+ if (enter == null) {
+ return NEED_MORE_INPUT;
+ }
+ LinkedList<String> history = (LinkedList<String>) channel.getAttribute(HISTORY_LIST_KEY);
+ Integer index = (Integer) channel.getAttribute(HISTORY_INDEX_KEY);
+ channel.removeAttribute(HISTORY_INDEX_KEY);
+ if (history != null && history.size() > 0 && index != null && index >= 0 && index < history.size()) {
+ String value = history.get(index);
+ if (value != null) {
+ byte[] b1 = value.getBytes();
+ if (message != null && message.length > 0) {
+ byte[] b2 = new byte[b1.length + message.length];
+ System.arraycopy(b1, 0, b2, 0, b1.length);
+ System.arraycopy(message, 0, b2, b1.length, message.length);
+ message = b2;
+ } else {
+ message = b1;
+ }
+ }
+ }
+ String result = toString(message, getCharset(channel));
+ if (result != null && result.trim().length() > 0) {
+ if (history == null) {
+ history = new LinkedList<String>();
+ channel.setAttribute(HISTORY_LIST_KEY, history);
+ }
+ if (history.size() == 0) {
+ history.addLast(result);
+ } else if (! result.equals(history.getLast())) {
+ history.remove(result);
+ history.addLast(result);
+ if (history.size() > 10) {
+ history.removeFirst();
+ }
+ }
+ }
+ return result;
+ }
+
+ private static boolean isClientSide(Channel channel) {
+ InetSocketAddress address = channel.getRemoteAddress();
+ URL url = channel.getUrl();
+ return url.getPort() == address.getPort() &&
+ NetUtils.filterLocalHost(url.getIp())
+ .equals(NetUtils.filterLocalHost(address.getAddress().getHostAddress()));
+ }
+
+ private static Charset getCharset(Channel channel) {
+ if (channel != null) {
+ Object attribute = channel.getAttribute(Constants.CHARSET_KEY);
+ if (attribute instanceof String) {
+ try {
+ return Charset.forName((String) attribute);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ } else if (attribute instanceof Charset) {
+ return (Charset) attribute;
+ }
+ URL url = channel.getUrl();
+ if (url != null) {
+ String parameter = url.getParameter(Constants.CHARSET_KEY);
+ if (parameter != null && parameter.length() > 0) {
+ try {
+ return Charset.forName(parameter);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ }
+ try {
+ return Charset.forName("GBK");
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ return Charset.defaultCharset();
+ }
+
+ private static String toString(byte[] message, Charset charset) throws UnsupportedEncodingException {
+ byte[] copy = new byte[message.length];
+ int index = 0;
+ for (int i = 0; i < message.length; i ++) {
+ byte b = message[i] ;
+ if (b == '\b') { // backspace
+ if (index > 0) {
+ index --;
+ }
+ if (i > 2 && message[i - 2] < 0) { // double byte char
+ if (index > 0) {
+ index --;
+ }
+ }
+ } else if (b == 27) { // escape
+ if (i < message.length - 4 && message[i + 4] == 126) {
+ i = i + 4;
+ } else if (i < message.length - 3 && message[i + 3] == 126) {
+ i = i + 3;
+ } else if (i < message.length - 2) {
+ i = i + 2;
+ }
+ } else if (b == -1 && i < message.length - 2
+ && (message[i + 1] == -3 || message[i + 1] == -5)) { // handshake
+ i = i + 2;
+ } else {
+ copy[index ++] = message[i];
+ }
+ }
+ if (index == 0) {
+ return "";
+ }
+ return new String(copy, 0, index, charset.name()).trim();
+ }
+
+ private static boolean isEquals(byte[] message, byte[] command) throws IOException {
+ return message.length == command.length && endsWith(message, command);
+ }
+
+ private static boolean endsWith(byte[] message, byte[] command) throws IOException {
+ if (message.length < command.length) {
+ return false;
+ }
+ int offset = message.length - command.length;
+ for (int i = command.length - 1; i >= 0 ; i --) {
+ if (message[offset + i] != command[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/Help.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/Help.java
new file mode 100644
index 0000000..8b6c1a9
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.java
new file mode 100644
index 0000000..87be74c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;
+
+/**
+ * TelnetHandlerDispather
+ *
+ * @author william.liangf
+ */
+public class TelnetHandlerAdapter extends ChannelHandlerAdapter implements TelnetHandler {
+
+ private final ExtensionLoader<TelnetHandler> extensionLoader = ExtensionLoader.getExtensionLoader(TelnetHandler.class);
+
+ public String telnet(Channel channel, String message) throws RemotingException {
+ String prompt = channel.getUrl().getParameterAndDecoded(Constants.PROMPT_KEY, Constants.DEFAULT_PROMPT);
+ boolean noprompt = message.contains("--no-prompt");
+ message = message.replace("--no-prompt", "");
+ StringBuilder buf = new StringBuilder();
+ message = message.trim();
+ String command;
+ if (message.length() > 0) {
+ int i = message.indexOf(' ');
+ if (i > 0) {
+ command = message.substring(0, i).trim();
+ message = message.substring(i + 1).trim();
+ } else {
+ command = message;
+ message = "";
+ }
+ } else {
+ command = "";
+ }
+ if (command.length() > 0) {
+ if (extensionLoader.hasExtension(command)) {
+ try {
+ String result = extensionLoader.getExtension(command).telnet(channel, message);
+ if (result == null) {
+ return null;
+ }
+ buf.append(result);
+ } catch (Throwable t) {
+ buf.append(t.getMessage());
+ }
+ } else {
+ buf.append("Unsupported command: ");
+ buf.append(command);
+ }
+ }
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ if (prompt != null && prompt.length() > 0 && ! noprompt) {
+ buf.append(prompt);
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetUtils.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetUtils.java
new file mode 100644
index 0000000..02aaa63
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java
new file mode 100644
index 0000000..13772e4
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support.command;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+
+/**
+ * ClearTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "[lines]", summary = "Clear screen.", detail = "Clear screen.")
+public class ClearTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ int lines = 100;
+ if (message.length() > 0) {
+ if (! StringUtils.isInteger(message)) {
+ return "Illegal lines " + message + ", must be integer.";
+ }
+ lines = Integer.parseInt(message);
+ }
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < lines; i ++) {
+ buf.append("\r\n");
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java
new file mode 100644
index 0000000..7b4d328
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support.command;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+
+/**
+ * ExitTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "", summary = "Exit the telnet.", detail = "Exit the telnet.")
+public class ExitTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ channel.close();
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java
new file mode 100644
index 0000000..d122792
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support.command;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.remoting.telnet.support.TelnetUtils;
+
+/**
+ * HelpTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "[command]", summary = "Show help.", detail = "Show help.")
+public class HelpTelnetHandler implements TelnetHandler {
+
+ private final ExtensionLoader<TelnetHandler> extensionLoader = ExtensionLoader.getExtensionLoader(TelnetHandler.class);
+
+ public String telnet(Channel channel, String message) {
+ if (message.length() > 0) {
+ if (! extensionLoader.hasExtension(message)) {
+ return "No such command " + message;
+ }
+ TelnetHandler handler = extensionLoader.getExtension(message);
+ Help help = handler.getClass().getAnnotation(Help.class);
+ StringBuilder buf = new StringBuilder();
+ buf.append("Command:\r\n ");
+ buf.append(message + " " + help.parameter().replace("\r\n", " ").replace("\n", " "));
+ buf.append("\r\nSummary:\r\n ");
+ buf.append(help.summary().replace("\r\n", " ").replace("\n", " "));
+ buf.append("\r\nDetail:\r\n ");
+ buf.append(help.detail().replace("\r\n", " \r\n").replace("\n", " \n"));
+ return buf.toString();
+ } else {
+ List<List<String>> table = new ArrayList<List<String>>();
+ List<TelnetHandler> handlers = extensionLoader.getActivateExtension(channel.getUrl(), "telnet");
+ if (handlers != null && handlers.size() > 0) {
+ for (TelnetHandler handler : handlers) {
+ Help help = handler.getClass().getAnnotation(Help.class);
+ List<String> row = new ArrayList<String>();
+ String parameter = " " + extensionLoader.getExtensionName(handler) + " " + (help != null ? help.parameter().replace("\r\n", " ").replace("\n", " ") : "");
+ row.add(parameter.length() > 50 ? parameter.substring(0, 50) + "..." : parameter);
+ String summary = help != null ? help.summary().replace("\r\n", " ").replace("\n", " ") : "";
+ row.add(summary.length() > 50 ? summary.substring(0, 50) + "..." : summary);
+ table.add(row);
+ }
+ }
+ return "Please input \"help [command]\" show detail.\r\n" + TelnetUtils.toList(table);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/LogTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/LogTelnetHandler.java
new file mode 100644
index 0000000..166ca0d
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/LogTelnetHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support.command;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Level;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+
+/**
+ * LogTelnetHandler
+ * @author chao.liuc
+ *
+ */
+@Activate
+@Help(parameter = "level", summary = "Change log level or show log ", detail = "Change log level or show log")
+public class LogTelnetHandler implements TelnetHandler {
+
+ public static final String SERVICE_KEY = "telnet.log";
+
+ public String telnet(Channel channel, String message) {
+ long size = 0 ;
+ File file = LoggerFactory.getFile();
+ StringBuffer buf = new StringBuffer();
+ if (message == null || message.trim().length() == 0) {
+ buf.append("EXAMPLE: log error / log 100");
+ }else {
+ String str[] = message.split(" ");
+ if (! StringUtils.isInteger(str[0])){
+ LoggerFactory.setLevel(Level.valueOf(message.toUpperCase()));
+ } else {
+ int SHOW_LOG_LENGTH = Integer.parseInt(str[0]);
+
+ if (file != null && file.exists()) {
+ try{
+ FileInputStream fis = new FileInputStream(file);
+ FileChannel filechannel = fis.getChannel();
+ size = filechannel.size();
+ ByteBuffer bb;
+ if (size <= SHOW_LOG_LENGTH) {
+ bb = ByteBuffer.allocate((int) size);
+ filechannel.read(bb, 0);
+ } else {
+ int pos = (int) (size - SHOW_LOG_LENGTH);
+ bb = ByteBuffer.allocate(SHOW_LOG_LENGTH);
+ filechannel.read(bb, pos);
+ }
+ bb.flip();
+ String content = new String(bb.array()).replace("<", "<")
+ .replace(">", ">").replace("\n", "<br/><br/>");
+ buf.append("\r\ncontent:"+content);
+
+ buf.append("\r\nmodified:"+(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+ .format(new Date(file.lastModified()))));
+ buf.append("\r\nsize:"+size +"\r\n");
+ }catch (Exception e) {
+ buf.append(e.getMessage());
+ }
+ }else {
+ size = 0;
+ buf.append("\r\nMESSAGE: log file not exists or log appender is console .");
+ }
+ }
+ }
+ buf.append("\r\nCURRENT LOG LEVEL:"+ LoggerFactory.getLevel())
+ .append("\r\nCURRENT LOG APPENDER:"+ (file == null ? "console" : file.getAbsolutePath()));
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
new file mode 100644
index 0000000..7014a6c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support.command;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.common.status.support.StatusUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.remoting.telnet.support.TelnetUtils;
+
+/**
+ * StatusTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "[-l]", summary = "Show status.", detail = "Show status.")
+public class StatusTelnetHandler implements TelnetHandler {
+
+ private final ExtensionLoader<StatusChecker> extensionLoader = ExtensionLoader.getExtensionLoader(StatusChecker.class);
+
+ public String telnet(Channel channel, String message) {
+ if (message.equals("-l")) {
+ List<StatusChecker> checkers = extensionLoader.getActivateExtension(channel.getUrl(), "status");
+ String[] header = new String[] {"resource", "status", "message"};
+ List<List<String>> table = new ArrayList<List<String>>();
+ Map<String, Status> statuses = new HashMap<String, Status>();
+ if (checkers != null && checkers.size() > 0) {
+ for (StatusChecker checker : checkers) {
+ String name = extensionLoader.getExtensionName(checker);
+ Status stat;
+ try {
+ stat = checker.check();
+ } catch (Throwable t) {
+ stat = new Status(Status.Level.ERROR, t.getMessage());
+ }
+ statuses.put(name, stat);
+ if (stat.getLevel() != null && stat.getLevel() != Status.Level.UNKNOWN) {
+ List<String> row = new ArrayList<String>();
+ row.add(name);
+ row.add(String.valueOf(stat.getLevel()));
+ row.add(stat.getMessage() == null ? "" : stat.getMessage());
+ table.add(row);
+ }
+ }
+ }
+ Status stat= StatusUtils.getSummaryStatus(statuses);
+ List<String> row = new ArrayList<String>();
+ row.add("summary");
+ row.add(String.valueOf(stat.getLevel()));
+ row.add(stat.getMessage());
+ table.add(row);
+ return TelnetUtils.toTable(header, table);
+ } else if (message.length() > 0) {
+ return "Unsupported parameter " + message + " for status.";
+ }
+ String status = channel.getUrl().getParameter("status");
+ Map<String, Status> statuses = new HashMap<String, Status>();
+ if (status != null && status.length() > 0) {
+ String[] ss = Constants.COMMA_SPLIT_PATTERN.split(status);
+ for (String s : ss) {
+ StatusChecker handler = extensionLoader.getExtension(s);
+ Status stat;
+ try {
+ stat = handler.check();
+ } catch (Throwable t) {
+ stat = new Status(Status.Level.ERROR, t.getMessage());
+ }
+ statuses.put(s, stat);
+ }
+ }
+ Status stat= StatusUtils.getSummaryStatus(statuses);
+ return String.valueOf(stat.getLevel());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractChannel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractChannel.java
new file mode 100644
index 0000000..0bd04ba
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractClient.java
new file mode 100644
index 0000000..b500850
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractClient.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelHandlers;
+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;
+
+/**
+ * AbstractClient
+ *
+ * @author qian.lei
+ * @author chao.liuc
+ */
+public abstract class AbstractClient extends AbstractEndpoint implements Client {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractClient.class);
+
+ protected static final String CLIENT_THREAD_POOL_NAME ="DubboClientHandler";
+
+ private static final AtomicInteger CLIENT_THREAD_POOL_ID = new AtomicInteger();
+
+ private final Lock connectLock = new ReentrantLock();
+
+ private static final ScheduledThreadPoolExecutor reconnectExecutorService = new ScheduledThreadPoolExecutor(2, new NamedThreadFactory("DubboClientReconnectTimer", true));
+
+ private volatile ScheduledFuture<?> reconnectExecutorFuture = null;
+
+ protected volatile ExecutorService executor;
+
+ private final boolean send_reconnect ;
+
+ private final AtomicInteger reconnect_count = new AtomicInteger(0);
+
+ //重连的error日志是否已经被调用过.
+ private final AtomicBoolean reconnect_error_log_flag = new AtomicBoolean(false) ;
+
+ //重连warning的间隔.(waring多少次之后,warning一次) //for test
+ private final int reconnect_warning_period ;
+
+ //the last successed connected time
+ private long lastConnectedTime = System.currentTimeMillis();
+
+ private final long shutdown_timeout ;
+
+
+ public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
+ super(url, handler);
+
+ send_reconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);
+
+ shutdown_timeout = url.getParameter(Constants.SHUTDOWN_TIMEOUT_KEY, Constants.DEFAULT_SHUTDOWN_TIMEOUT);
+
+ //默认重连间隔2s,1800表示1小时warning一次.
+ reconnect_warning_period = url.getParameter("reconnect.waring.period", 1800);
+
+ try {
+ doOpen();
+ } catch (Throwable t) {
+ close();
+ throw new RemotingException(url.toInetSocketAddress(), null,
+ "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+ + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
+ }
+ try {
+ // connect.
+ connect();
+ if (logger.isInfoEnabled()) {
+ logger.info("Start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress());
+ }
+ } catch (RemotingException t) {
+ if (url.getParameter(Constants.CHECK_KEY, true)) {
+ close();
+ throw t;
+ } else {
+ logger.error("Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+ + " connect to the server " + getRemoteAddress() + " (check == false, ignore and retry later!), cause: " + t.getMessage(), t);
+ }
+ } catch (Throwable t){
+ close();
+ throw new RemotingException(url.toInetSocketAddress(), null,
+ "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+ + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
+ }
+
+ if (handler instanceof WrappedChannelHandler ){
+ executor = ((WrappedChannelHandler)handler).getExecutor();
+ }
+ }
+
+ protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler){
+ url = url.addParameterIfAbsent(Constants.THREAD_NAME_KEY, CLIENT_THREAD_POOL_NAME)
+ .addParameterIfAbsent(Constants.THREADPOOL_KEY, Constants.DEFAULT_CLIENT_THREADPOOL);
+ return ChannelHandlers.wrap(handler, url);
+ }
+
+ /**
+ * init reconnect thread
+ */
+ private synchronized void initConnectStatusCheckCommand(){
+ //reconnect=false to close reconnect
+ int reconnect = getReconnectParam(getUrl());
+ if(reconnect > 0 && (reconnectExecutorFuture == null || reconnectExecutorFuture.isCancelled())){
+ 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();
+ // 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 ( reconnect_count.getAndIncrement() % 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);
+ }
+
+ protected 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.");
+ } else {
+ if (logger.isInfoEnabled()){
+ logger.info("Successed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+ + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+ + ", channel is " + this.getChannel());
+ }
+ }
+ 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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractCodec.java
new file mode 100644
index 0000000..4082b86
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractCodec.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.remoting.transport;
+
+import java.io.IOException;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.serialize.Serialization;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.Codec;
+
+/**
+ * AbstractCodec
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractCodec implements Codec {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ 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) {
+ IOException e = new IOException("Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel);
+ logger.error(e);
+ throw e;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractEndpoint.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractEndpoint.java
new file mode 100644
index 0000000..db493fc4
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractEndpoint.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Resetable;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Codec;
+
+/**
+ * AbstractEndpoint
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractEndpoint extends AbstractPeer implements Resetable {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractEndpoint.class);
+
+ private Codec codec;
+
+ private int timeout;
+
+ private int connectTimeout;
+
+ public AbstractEndpoint(URL url, ChannelHandler handler) {
+ super(url, handler);
+ this.codec = ExtensionLoader.getExtensionLoader(Codec.class).getExtension(url.getParameter(Constants.CODEC_KEY, "telnet"));
+ this.timeout = url.getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractPeer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractPeer.java
new file mode 100644
index 0000000..353d54a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.java
new file mode 100644
index 0000000..1b9a2c4
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;
+
+/**
+ * AbstractServer
+ *
+ * @author qian.lei
+ * @author ding.lid
+ */
+public abstract class AbstractServer extends AbstractEndpoint implements Server {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractServer.class);
+
+ private InetSocketAddress localAddress;
+
+ private InetSocketAddress bindAddress;
+
+ private int accepts;
+
+ private int idleTimeout = 600; //600 seconds
+
+ protected static final String SERVER_THREAD_POOL_NAME ="DubboServerHandler";
+
+ ExecutorService executor;
+
+ public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
+ super(url, handler);
+ localAddress = getUrl().toInetSocketAddress();
+ String host = url.getParameter(Constants.ANYHOST_KEY, false)
+ || NetUtils.isInvalidLocalHost(getUrl().getHost())
+ ? NetUtils.ANYHOST : getUrl().getHost();
+ bindAddress = new InetSocketAddress(host, getUrl().getPort());
+ this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
+ this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
+ try {
+ doOpen();
+ if (logger.isInfoEnabled()) {
+ logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
+ }
+ } catch (Throwable t) {
+ throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
+ + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
+ }
+ if (handler instanceof WrappedChannelHandler ){
+ executor = ((WrappedChannelHandler)handler).getExecutor();
+ }
+ }
+
+ protected abstract void doOpen() throws Throwable;
+
+ protected abstract void doClose() throws Throwable;
+
+ public void reset(URL url) {
+ if (url == null) {
+ return;
+ }
+ try {
+ if (url.hasParameter(Constants.ACCEPTS_KEY)) {
+ int a = url.getParameter(Constants.ACCEPTS_KEY, 0);
+ if (a > 0) {
+ this.accepts = a;
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ try {
+ if (url.hasParameter(Constants.IDLE_TIMEOUT_KEY)) {
+ int t = url.getParameter(Constants.IDLE_TIMEOUT_KEY, 0);
+ if (t > 0) {
+ this.idleTimeout = t;
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ try {
+ if (url.hasParameter(Constants.THREADS_KEY)
+ && executor instanceof ThreadPoolExecutor && !executor.isShutdown()) {
+ ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
+ int threads = url.getParameter(Constants.THREADS_KEY, 0);
+ int max = threadPoolExecutor.getMaximumPoolSize();
+ int core = threadPoolExecutor.getCorePoolSize();
+ if (threads > 0 && (threads != max || threads != core)) {
+ if (threads < core) {
+ threadPoolExecutor.setCorePoolSize(threads);
+ if (core == max) {
+ threadPoolExecutor.setMaximumPoolSize(threads);
+ }
+ } else {
+ threadPoolExecutor.setMaximumPoolSize(threads);
+ if (core == max) {
+ threadPoolExecutor.setCorePoolSize(threads);
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ super.setUrl(getUrl().addParameters(url.getParameters()));
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ Collection<Channel> channels = getChannels();
+ for (Channel channel : channels) {
+ if (channel.isConnected()) {
+ channel.send(message, sent);
+ }
+ }
+ }
+
+ public void close() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Close " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
+ }
+ ExecutorUtil.shutdownNow(executor ,100);
+ try {
+ super.close();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ doClose();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public void close(int timeout) {
+ ExecutorUtil.gracefulShutdown(executor ,timeout);
+ close();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return localAddress;
+ }
+
+ public InetSocketAddress getBindAddress() {
+ return bindAddress;
+ }
+
+ public int getAccepts() {
+ return accepts;
+ }
+
+ public int getIdleTimeout() {
+ return idleTimeout;
+ }
+
+ @Override
+ public void connected(Channel ch) throws RemotingException {
+ Collection<Channel> channels = getChannels();
+ if (accepts > 0 && channels.size() > accepts) {
+ logger.error("Close channel " + ch + ", cause: The server " + ch.getLocalAddress() + " connections greater than max config " + accepts);
+ ch.close();
+ return;
+ }
+ super.connected(ch);
+ }
+
+ @Override
+ public void disconnected(Channel ch) throws RemotingException {
+ Collection<Channel> channels = getChannels();
+ if (channels.size() == 0){
+ logger.warn("All clients has discontected from " + ch.getLocalAddress() + ". You can graceful shutdown now.");
+ }
+ super.disconnected(ch);
+ }
+
+ protected Codec getDownstreamCodec() {
+ Codec downstreamCodec = getCodec();
+ String downstreamCodecStr = getUrl().getParameter(Constants.DOWNSTREAM_CODEC_KEY);
+ if(downstreamCodecStr != null ){
+ downstreamCodec = ExtensionLoader.getExtensionLoader(Codec.class).getExtension(downstreamCodecStr);
+ }
+ return downstreamCodec;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelDelegate.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelDelegate.java
new file mode 100644
index 0000000..639f95f
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerAdapter.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerAdapter.java
new file mode 100644
index 0000000..f77605e
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDelegate.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDelegate.java
new file mode 100644
index 0000000..b618761
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDispatcher.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDispatcher.java
new file mode 100644
index 0000000..76fdd56
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ClientDelegate.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ClientDelegate.java
new file mode 100644
index 0000000..6bb445d
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ServerDelegate.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/ServerDelegate.java
new file mode 100644
index 0000000..e91572e
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java
new file mode 100644
index 0000000..8659785
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.codec;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.transport.AbstractCodec;
+
+/**
+ * TransportCodec
+ *
+ * @author william.liangf
+ */
+public class TransportCodec extends AbstractCodec {
+
+ public void encode(Channel channel, OutputStream output, Object message) throws IOException {
+ ObjectOutput objectOutput = getSerialization(channel).serialize(channel.getUrl(), output);
+ encodeData(channel, objectOutput, message);
+ objectOutput.flushBuffer();
+ }
+
+ public Object decode(Channel channel, InputStream input) throws IOException {
+ return decodeData(channel, getSerialization(channel).deserialize(channel.getUrl(), input));
+ }
+
+ protected void encodeData(Channel channel, ObjectOutput output, Object message) throws IOException {
+ encodeData(output, message);
+ }
+
+ protected Object decodeData(Channel channel, ObjectInput input) throws IOException {
+ return decodeData(input);
+ }
+
+ protected void encodeData(ObjectOutput output, Object message) throws IOException {
+ output.writeObject(message);
+ }
+
+ protected Object decodeData(ObjectInput input) throws IOException {
+ try {
+ return input.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new IOException("ClassNotFoundException: " + StringUtils.toString(e));
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelEventRunnable.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelEventRunnable.java
new file mode 100644
index 0000000..e637d92
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelEventRunnable.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class ChannelEventRunnable implements Runnable {
+ private final ChannelHandler handler;
+ private final Channel channel;
+ private final ChannelState state;
+ private final Throwable exception;
+ private final Object message;
+
+ public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state) {
+ this(channel, handler, state, null);
+ }
+
+ public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Object message) {
+ this(channel, handler, state, message, null);
+ }
+
+ public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Throwable t) {
+ this(channel, handler, state, null , t);
+ }
+
+ public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Object message, Throwable exception) {
+ this.channel = channel;
+ this.handler = handler;
+ this.state = state;
+ this.message = message;
+ this.exception = exception;
+ }
+
+ public void run() {
+ switch (state) {
+ case CONNECTED:
+ try{
+ handler.connected(channel);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel,e);
+ }
+ break;
+ case DISCONNECTED:
+ try{
+ handler.disconnected(channel);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel,e);
+ }
+ break;
+ case SENT:
+ try{
+ handler.sent(channel,message);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel+",message is "+ message,e);
+ }
+
+ break;
+ case RECEIVED:
+ try{
+ handler.received(channel, message);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel+",message is "+ message,e);
+ }
+ break;
+ case CAUGHT:
+ try{
+ handler.caught(channel, exception);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel +", message is: "+message+" exception is "+exception,e);
+ }
+
+ break;
+ }
+ }
+
+ /**
+ * ChannelState
+ *
+ * @author william.liangf
+ */
+ public enum ChannelState{
+
+ /**
+ * CONNECTED
+ */
+ CONNECTED,
+
+ /**
+ * DISCONNECTED
+ */
+ DISCONNECTED,
+
+ /**
+ * SENT
+ */
+ SENT,
+
+ /**
+ * RECEIVED
+ */
+ RECEIVED,
+
+ /**
+ * CAUGHT
+ */
+ CAUGHT
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelHandlers.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelHandlers.java
new file mode 100644
index 0000000..f733a1a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/ChannelHandlers.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather;
+
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Dispather;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class ChannelHandlers {
+
+ public static ChannelHandler wrap(ChannelHandler handler, URL url){
+ return ExtensionLoader.getExtensionLoader(Dispather.class)
+ .getAdaptiveExtension().dispath(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/WrappedChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/WrappedChannelHandler.java
new file mode 100644
index 0000000..56b9cb8
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/WrappedChannelHandler.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.threadpool.ThreadPool;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerDelegate;
+
+public class WrappedChannelHandler implements ChannelHandlerDelegate {
+
+ protected static final Logger logger = LoggerFactory.getLogger(WrappedChannelHandler.class);
+
+ protected static final ExecutorService SHARED_EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory("DubboSharedHandler", true));
+
+ protected final ExecutorService executor;
+
+ protected final ChannelHandler handler;
+
+ protected final URL url;
+
+ public WrappedChannelHandler(ChannelHandler handler, URL url) {
+ this.handler = handler;
+ this.url = url;
+ executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
+ }
+
+ public void close() {
+ try {
+ if (executor instanceof ExecutorService) {
+ ((ExecutorService)executor).shutdown();
+ }
+ } catch (Throwable t) {
+ logger.warn("fail to destroy thread pool of server: " + t.getMessage(), t);
+ }
+ }
+
+ public void connected(Channel channel) throws RemotingException {
+ handler.connected(channel);
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ handler.disconnected(channel);
+ }
+
+ public void sent(Channel channel, Object message) throws RemotingException {
+ handler.sent(channel, message);
+ }
+
+ @SuppressWarnings("deprecation")
+ public void received(Channel channel, Object message) throws RemotingException {
+ if (message instanceof Request && ((Request)message).isHeartbeat()){
+ Request req = (Request) message;
+ if (req.isTwoWay()){
+ Response res = new Response(req.getId(),req.getVersion());
+ res.setHeartbeat(true);
+ channel.send(res);
+ }
+ }
+ handler.received(channel, message);
+ }
+
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+ handler.caught(channel, exception);
+ }
+
+ public ExecutorService getExecutor() {
+ return executor;
+ }
+
+ public ChannelHandler getHandler() {
+ if (handler instanceof ChannelHandlerDelegate) {
+ return ((ChannelHandlerDelegate) handler).getHandler();
+ } else {
+ return handler;
+ }
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllChannelHandler.java
new file mode 100644
index 0000000..4c5828d
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllChannelHandler.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather.all;
+
+import java.util.concurrent.ExecutorService;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ExecutionException;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable;
+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable.ChannelState;
+
+public class AllChannelHandler extends WrappedChannelHandler {
+
+ public AllChannelHandler(ChannelHandler handler, URL url) {
+ super(handler, url);
+ }
+
+ public void connected(Channel channel) throws RemotingException {
+ ExecutorService cexecutor = getExecutorService();
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CONNECTED));
+ }catch (Throwable t) {
+ throw new ExecutionException("connect event", channel, getClass()+" error when process connected event ." , t);
+ }
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ ExecutorService cexecutor = getExecutorService();
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.DISCONNECTED));
+ }catch (Throwable t) {
+ throw new ExecutionException("disconnect event", channel, getClass()+" error when process disconnected event ." , t);
+ }
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ //FIXME 包的依赖顺序有问题
+ if (message instanceof Request && ((Request)message).isEvent()){
+ super.received(channel, message);
+ return;
+ }
+ ExecutorService cexecutor = getExecutorService();
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+ }catch (Throwable t) {
+ throw new ExecutionException(message, channel, getClass()+" error when process received event ." , t);
+ }
+ }
+
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+ ExecutorService cexecutor = getExecutorService();
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CAUGHT, exception));
+ }catch (Throwable t) {
+ throw new ExecutionException("caught event", channel, getClass()+" error when process caught event ." , t);
+ }
+ }
+
+ private ExecutorService getExecutorService() {
+ ExecutorService cexecutor = executor;
+ if (cexecutor == null || cexecutor.isShutdown()) {
+ cexecutor = SHARED_EXECUTOR;
+ }
+ return cexecutor;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllDispather.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllDispather.java
new file mode 100644
index 0000000..a5f77ed
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/all/AllDispather.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather.all;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Dispather;
+
+/**
+ * 默认的线程池配置
+ *
+ * @author chao.liuc
+ */
+public class AllDispather implements Dispather {
+
+ public static final String NAME = "all";
+
+ public ChannelHandler dispath(ChannelHandler handler, URL url) {
+ return new AllChannelHandler(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedChannelHandler.java
new file mode 100644
index 0000000..6e175e7
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedChannelHandler.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather.connection;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ExecutionException;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable;
+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable.ChannelState;
+
+public class ConnectionOrderedChannelHandler extends WrappedChannelHandler {
+
+ protected final ThreadPoolExecutor connectionExecutor;
+ private final int queuewarninglimit ;
+
+ public ConnectionOrderedChannelHandler(ChannelHandler handler, URL url) {
+ super(handler, url);
+ String threadName = url.getParameter(Constants.THREAD_NAME_KEY,Constants.DEFAULT_THREAD_NAME);
+ connectionExecutor = new ThreadPoolExecutor(1, 1,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>(url.getPositiveParameter(Constants.CONNECT_QUEUE_CAPACITY, Integer.MAX_VALUE)),
+ new NamedThreadFactory(threadName, true),
+ new AbortPolicyWithReport(threadName, url)
+ ); // FIXME 没有地方释放connectionExecutor!
+ queuewarninglimit = url.getParameter(Constants.CONNECT_QUEUE_WARNING_SIZE, Constants.DEFAULT_CONNECT_QUEUE_WARNING_SIZE);
+ }
+
+ public void connected(Channel channel) throws RemotingException {
+ try{
+ checkQueueLength();
+ connectionExecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CONNECTED));
+ }catch (Throwable t) {
+ throw new ExecutionException("connect event", channel, getClass()+" error when process connected event ." , t);
+ }
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ try{
+ checkQueueLength();
+ connectionExecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.DISCONNECTED));
+ }catch (Throwable t) {
+ throw new ExecutionException("disconnected event", channel, getClass()+" error when process disconnected event ." , t);
+ }
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ //FIXME 包的依赖顺序有问题
+ if (message instanceof Request && ((Request)message).isEvent()){
+ super.received(channel, message);
+ return;
+ }
+
+ ExecutorService cexecutor = executor;
+ if (cexecutor == null || cexecutor.isShutdown()) {
+ cexecutor = SHARED_EXECUTOR;
+ }
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+ }catch (Throwable t) {
+ throw new ExecutionException(message, channel, getClass()+" error when process received event ." , t);
+ }
+ }
+
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+ ExecutorService cexecutor = executor;
+ if (cexecutor == null || cexecutor.isShutdown()) {
+ cexecutor = SHARED_EXECUTOR;
+ }
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CAUGHT, exception));
+ }catch (Throwable t) {
+ throw new ExecutionException("caught event", channel, getClass()+" error when process caught event ." , t);
+ }
+ }
+
+ private void checkQueueLength(){
+ if (connectionExecutor.getQueue().size() > queuewarninglimit){
+ logger.warn(new IllegalThreadStateException("connectionordered channel handler `queue size: "+connectionExecutor.getQueue().size()+" exceed the warning limit number :"+queuewarninglimit));
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedDispather.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedDispather.java
new file mode 100644
index 0000000..8824299
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/connection/ConnectionOrderedDispather.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather.connection;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Dispather;
+
+/**
+ * connect disconnect 保证顺序.
+ *
+ * @author chao.liuc
+ */
+public class ConnectionOrderedDispather implements Dispather {
+
+ public static final String NAME = "connection";
+
+ public ChannelHandler dispath(ChannelHandler handler, URL url) {
+ return new ConnectionOrderedChannelHandler(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/direct/DirectDispather.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/direct/DirectDispather.java
new file mode 100644
index 0000000..5edbfb7
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/direct/DirectDispather.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather.direct;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Dispather;
+
+/**
+ * 不派发线程池。
+ *
+ * @author chao.liuc
+ */
+public class DirectDispather implements Dispather {
+
+ public static final String NAME = "direct";
+
+ public ChannelHandler dispath(ChannelHandler handler, URL url) {
+ return handler;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionChannelHandler.java
new file mode 100644
index 0000000..48ed260
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionChannelHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather.execution;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable;
+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable.ChannelState;
+
+public class ExecutionChannelHandler extends WrappedChannelHandler {
+
+ public ExecutionChannelHandler(ChannelHandler handler, URL url) {
+ super(handler, url);
+ }
+
+ public void connected(Channel channel) throws RemotingException {
+ executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CONNECTED));
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.DISCONNECTED));
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ //FIXME 包的依赖顺序有问题
+ if (message instanceof Request && ((Request)message).isEvent()){
+ super.received(channel, message);
+ return;
+ }
+ executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+ }
+
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+ executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CAUGHT, exception));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionDispather.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionDispather.java
new file mode 100644
index 0000000..64f0901
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/execution/ExecutionDispather.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather.execution;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Dispather;
+
+/**
+ * 除发送全部使用线程池处理
+ *
+ * @author chao.liuc
+ */
+public class ExecutionDispather implements Dispather {
+
+ public static final String NAME = "execution";
+
+ public ChannelHandler dispath(ChannelHandler handler, URL url) {
+ return new ExecutionChannelHandler(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyChannelHandler.java
new file mode 100644
index 0000000..b7df172
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyChannelHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather.message;
+
+import java.util.concurrent.ExecutorService;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ExecutionException;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable;
+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelEventRunnable.ChannelState;
+
+public class MessageOnlyChannelHandler extends WrappedChannelHandler {
+
+ public MessageOnlyChannelHandler(ChannelHandler handler, URL url) {
+ super(handler, url);
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ //FIXME 包的依赖顺序有问题
+ if (message instanceof Request && ((Request)message).isEvent()){
+ super.received(channel, message);
+ return;
+ }
+
+ ExecutorService cexecutor = executor;
+ if (cexecutor == null || cexecutor.isShutdown()) {
+ cexecutor = SHARED_EXECUTOR;
+ }
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+ }catch (Throwable t) {
+ throw new ExecutionException(message, channel, getClass()+" error when process received event ." , t);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyDispather.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyDispather.java
new file mode 100644
index 0000000..574e8ea
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispather/message/MessageOnlyDispather.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.dispather.message;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Dispather;
+
+/**
+ * 只有message receive使用线程池.
+ *
+ * @author chao.liuc
+ */
+public class MessageOnlyDispather implements Dispather {
+
+ public static final String NAME = "message";
+
+ public ChannelHandler dispath(ChannelHandler handler, URL url) {
+ return new MessageOnlyChannelHandler(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec
new file mode 100644
index 0000000..d72ce44
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec
@@ -0,0 +1,3 @@
+transport=com.alibaba.dubbo.remoting.transport.codec.TransportCodec
+telnet=com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec
+exchange=com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Dispather b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Dispather
new file mode 100644
index 0000000..3eeb5d9
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Dispather
@@ -0,0 +1,5 @@
+all=com.alibaba.dubbo.remoting.transport.dispather.all.AllDispather
+direct=com.alibaba.dubbo.remoting.transport.dispather.direct.DirectDispather
+message=com.alibaba.dubbo.remoting.transport.dispather.message.MessageOnlyDispather
+execution=com.alibaba.dubbo.remoting.transport.dispather.execution.ExecutionDispather
+connection=com.alibaba.dubbo.remoting.transport.dispather.connection.ConnectionOrderedDispather
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger
new file mode 100644
index 0000000..c07fd67
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger
@@ -0,0 +1 @@
+header=com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler
new file mode 100644
index 0000000..c56ce82a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler
@@ -0,0 +1,5 @@
+clear=com.alibaba.dubbo.remoting.telnet.support.command.ClearTelnetHandler
+exit=com.alibaba.dubbo.remoting.telnet.support.command.ExitTelnetHandler
+help=com.alibaba.dubbo.remoting.telnet.support.command.HelpTelnetHandler
+status=com.alibaba.dubbo.remoting.telnet.support.command.StatusTelnetHandler
+log=com.alibaba.dubbo.remoting.telnet.support.command.LogTelnetHandler
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/ChanelHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/ChanelHandlerTest.java
new file mode 100644
index 0000000..1917fd6
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/DemoService.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/DemoService.java
new file mode 100644
index 0000000..29cadad
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/DemoServiceImpl.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/DemoServiceImpl.java
new file mode 100644
index 0000000..a9a90e6
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/Main.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/Main.java
new file mode 100644
index 0000000..f078531
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/MockResult.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/MockResult.java
new file mode 100644
index 0000000..e8f9376
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientCloseTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientCloseTest.java
new file mode 100644
index 0000000..8194562
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientFixedTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientFixedTest.java
new file mode 100644
index 0000000..6b93ca6
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientMain.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientMain.java
new file mode 100644
index 0000000..1819601
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientTest.java
new file mode 100644
index 0000000..658de26
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerMain.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerMain.java
new file mode 100644
index 0000000..55038a3
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerTest.java
new file mode 100644
index 0000000..3d117a7
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import com.alibaba.dubbo.remoting.transport.dispather.execution.ExecutionDispather;
+
+/**
+ * PerformanceServer
+ *
+ * mvn clean test -Dtest=*PerformanceServerTest -Dport=9911
+ *
+ * @author william.liangf
+ */
+public class PerformanceServerTest extends TestCase {
+
+ private static final Logger logger = LoggerFactory.getLogger(PerformanceServerTest.class);
+ private static ExchangeServer server = null;
+ @Test
+ public void testServer() throws Exception {
+ // 读取参数
+ if (PerformanceUtils.getProperty("port", null) == null) {
+ logger.warn("Please set -Dport=9911");
+ return;
+ }
+ final int port = PerformanceUtils.getIntProperty("port", 9911);
+ final boolean telnet = PerformanceUtils.getBooleanProperty("telnet", true);
+ if(telnet)statTelnetServer(port+1);
+ server = statServer();
+
+ synchronized (PerformanceServerTest.class) {
+ while (true) {
+ try {
+ PerformanceServerTest.class.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ private static void restartServer(int times,int alive,int sleep) throws Exception{
+ if(server!=null && !server.isClosed()){
+ server.close();
+ Thread.sleep(100);
+ }
+
+ for(int i=0;i<times;i++){
+ logger.info("restart times:"+i);
+ server = statServer();
+ if(alive>0)Thread.sleep(alive);
+ server.close();
+ if(sleep>0)Thread.sleep(sleep);
+ }
+
+ server = statServer();
+ }
+
+ private static ExchangeServer statServer() throws Exception{
+ final int port = PerformanceUtils.getIntProperty("port", 9911);
+ final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final String threadpool = PerformanceUtils.getProperty(Constants.THREADPOOL_KEY, Constants.DEFAULT_THREADPOOL);
+ final int threads = PerformanceUtils.getIntProperty(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
+ final int iothreads = PerformanceUtils.getIntProperty(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS);
+ final int buffer = PerformanceUtils.getIntProperty(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);
+ final String channelHandler = PerformanceUtils.getProperty(Constants.CHANNEL_HANDLER_KEY, ExecutionDispather.NAME);
+
+
+ // 启动服务器
+ ExchangeServer server = Exchangers.bind("exchange://0.0.0.0:" + port + "?transporter="
+ + transporter + "&serialization="
+ + serialization + "&threadpool=" + threadpool
+ + "&threads=" + threads + "&iothreads=" + iothreads +"&buffer="+buffer +"&channel.handler="+channelHandler, new ExchangeHandlerAdapter() {
+ public String telnet(Channel channel, String message) throws RemotingException {
+ return "echo: " + message + "\r\ntelnet> ";
+ }
+ public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
+ if ("environment".equals(request)) {
+ return PerformanceUtils.getEnvironment();
+ }
+ if ("scene".equals(request)) {
+ List<String> scene = new ArrayList<String>();
+ scene.add("Transporter: " + transporter);
+ scene.add("Service Threads: " + threads);
+ return scene;
+ }
+ return request;
+ }
+ });
+
+ return server;
+ }
+
+ private static ExchangeServer statTelnetServer(int port) throws Exception{
+ // 启动服务器
+ ExchangeServer telnetserver = Exchangers.bind("exchange://0.0.0.0:" + port , new ExchangeHandlerAdapter() {
+ public String telnet(Channel channel, String message) throws RemotingException {
+ if(message.equals("help")){
+ return "support cmd: \r\n\tstart \r\n\tstop \r\n\tshutdown \r\n\trestart times [alive] [sleep] \r\ntelnet>";
+ } else if(message.equals("stop")){
+ logger.info("server closed:"+server);
+ server.close();
+ return "stop server\r\ntelnet>";
+ } else if(message.startsWith("start")){
+ try {
+ restartServer(0,0,0);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "start server\r\ntelnet>";
+ } else if(message.startsWith("shutdown")){
+ System.exit(0);
+ return "start server\r\ntelnet>";
+ } else if(message.startsWith("channels")){
+ return "server.getExchangeChannels():"+server.getExchangeChannels().size()+"\r\ntelnet>";
+ } else if(message.startsWith("restart ")){ //r times [sleep] r 10 or r 10 100
+ String[] args = message.split(" ");
+ int times = Integer.parseInt(args[1]);
+ int alive = args.length>2?Integer.parseInt(args[2]):0;
+ int sleep = args.length>3?Integer.parseInt(args[3]):100;
+ try {
+ restartServer(times,alive,sleep);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return "restart server,times:"+times+" stop alive time: "+alive+",sleep time: " + sleep+" usage:r times [alive] [sleep] \r\ntelnet>";
+ } else{
+ return "echo: " + message + "\r\ntelnet> ";
+ }
+
+ }
+ });
+
+ return telnetserver;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceUtils.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/PerformanceUtils.java
new file mode 100644
index 0000000..f74cc8c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/RpcMessage.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/RpcMessage.java
new file mode 100755
index 0000000..8ab2c36
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/RpcMessageHandler.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/RpcMessageHandler.java
new file mode 100755
index 0000000..0a12593
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/TelnetServer.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/TelnetServer.java
new file mode 100644
index 0000000..637c9ce
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/codec/AbstractMockChannel.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/codec/AbstractMockChannel.java
new file mode 100644
index 0000000..325a50c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/codec/ExchangeCodecTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/codec/ExchangeCodecTest.java
new file mode 100644
index 0000000..44536cb
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/codec/ExchangeCodecTest.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.codec;
+
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec;
+import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec;
+
+/**
+ * @author chao.liuc
+ * byte 16
+ * 0-1 magic code
+ * 2 flag
+ * 8 - 1-request/0-response
+ * 7 - two way
+ * 6 - heartbeat
+ * 1-5 serialization id
+ * 3 status
+ * 20 ok
+ * 90 error?
+ * 4-11 id (long)
+ * 12 -15 datalength
+ *
+ */
+public class ExchangeCodecTest extends TelnetCodecTest{
+ // magic header.
+ private static final short MAGIC = (short) 0xdabb;
+ private static final byte MAGIC_HIGH = (byte) Bytes.short2bytes(MAGIC)[0];
+ private static final byte MAGIC_LOW = (byte) Bytes.short2bytes(MAGIC)[1];
+ Serialization serialization = getSerialization(Constants.DEFAULT_REMOTING_SERIALIZATION);
+
+
+
+ private Object decode(byte[] request) throws IOException{
+ InputStream input = new UnsafeByteArrayInputStream(request);
+ AbstractMockChannel channel = getServerSideChannel(url);
+ //decode
+ Object obj = codec.decode(channel, input);
+ return obj;
+ }
+
+ private byte[] getRequestBytes(Object obj, byte[] header) throws IOException{
+ // encode request data.
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ ObjectOutput out = serialization.serialize(url, bos);
+ out.writeObject(obj);
+
+ out.flushBuffer();
+ bos.flush();
+ bos.close();
+ byte[] data = bos.toByteArray();
+ byte[] len = Bytes.int2bytes(data.length);
+ System.arraycopy(len, 0, header, 12, 4);
+ byte[] request = join(header, data);
+ return request;
+ }
+
+ private byte[] assemblyDataProtocol(byte[] header){
+ Person request = new Person();
+ byte[] newbuf = join(header, objectToByte(request));
+ return newbuf;
+ }
+
+ private static Serialization getSerialization(String name) {
+ Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name);
+ return serialization;
+ }
+ //===================================================================================
+
+ @Before
+ public void setUp() throws Exception {
+ codec = new ExchangeCodec();
+ }
+ @Test
+ public void test_Decode_Error_MagicNum() throws IOException{
+ HashMap<byte[] , Object> inputBytes = new HashMap<byte[] , Object>();
+ inputBytes.put( new byte[] { 0 }, TelnetCodec.NEED_MORE_INPUT );
+ inputBytes.put( new byte[] { MAGIC_HIGH, 0 }, TelnetCodec.NEED_MORE_INPUT );
+ inputBytes.put( new byte[] { 0 , MAGIC_LOW }, TelnetCodec.NEED_MORE_INPUT );
+
+ for (byte[] input : inputBytes.keySet()){
+ testDecode_assertEquals(assemblyDataProtocol(input) ,inputBytes.get(input));
+ }
+ }
+
+ @Test
+ public void test_Decode_Error_Length() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0x20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Channel channel = getServerSideChannel(url);
+ byte[] baddata = new byte[]{1,2};
+ UnsafeByteArrayInputStream input = new UnsafeByteArrayInputStream(join(request, baddata));
+ Response obj = (Response)codec.decode(channel, input);
+ Assert.assertEquals(person, obj.getResult());
+ System.out.println(input.available());
+ //only decode necessary bytes
+ Assert.assertEquals(request.length, input.position());
+ }
+ @Test
+ public void test_Decode_Error_Response_Object() throws IOException{
+ //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0x20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+ //bad object
+ byte[] badbytes = new byte[]{-1,-2,-3,-4,-3,-4,-3,-4,-3,-4,-3,-4};
+ System.arraycopy(badbytes, 0, request, 21, badbytes.length);
+
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(90, obj.getStatus());
+ }
+ @Test
+ public void test_Decode_Check_Payload() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH , MAGIC_LOW , 1 ,1 ,1 ,1 ,1 , 1 ,1 ,1 ,1 ,1 , 1 ,1 , 1,1 };
+ byte[] request = assemblyDataProtocol(header) ;
+ try{
+ testDecode_assertEquals(request, TelnetCodec.NEED_MORE_INPUT);
+ fail();
+ }catch (IOException expected) {
+ Assert.assertTrue(expected.getMessage().startsWith("Data length too large: "+Bytes.bytes2int(new byte[]{1,1,1,1})));
+ }
+ }
+ @Test
+ public void test_Decode_Header_Need_Readmore() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH , MAGIC_LOW , 0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 };
+ testDecode_assertEquals(header, TelnetCodec.NEED_MORE_INPUT);
+ }
+
+ @Test
+ public void test_Decode_Body_Need_Readmore() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH , MAGIC_LOW , 0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0, 0, 0, 0 , 1 ,1, 'a', 'a' };
+ testDecode_assertEquals(header, TelnetCodec.NEED_MORE_INPUT);
+ }
+ @Test
+ public void test_Decode_MigicCodec_Contain_ExchangeHeader() throws IOException{
+ //
+ byte[] header = new byte[] { 0, 0, MAGIC_HIGH , MAGIC_LOW , 0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 };
+
+ Channel channel = getServerSideChannel(url);
+ UnsafeByteArrayInputStream input = new UnsafeByteArrayInputStream(header);
+ Object obj = codec.decode(channel, input);
+ Assert.assertEquals(TelnetCodec.NEED_MORE_INPUT, obj);
+ //如果telnet数据与request数据在同一个数据包中,不能因为telnet没有结尾字符而影响其他数据的接收.
+ Assert.assertEquals(2, input.position());
+ }
+
+ @Test
+ public void test_Decode_Return_Response_Person() throws IOException{
+ //00000010-response/oneway/hearbeat=false/hessian |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(20, obj.getStatus());
+ Assert.assertEquals(person, obj.getResult());
+ System.out.println(obj);
+ }
+
+ @Test //status输入有问题,序列化时读取信息出错.
+ public void test_Decode_Return_Response_Error() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 2, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ String errorString = "encode request data error ";
+ byte[] request = getRequestBytes(errorString, header);
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(90, obj.getStatus());
+ Assert.assertEquals(errorString, obj.getErrorMessage());
+ }
+ @Test
+ public void test_Decode_Return_Request_Event_Object() throws IOException{
+ //|10011111|20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) 0xff, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Request obj = (Request)decode(request);
+ Assert.assertEquals(person, obj.getData());
+ Assert.assertEquals(true, obj.isTwoWay());
+ Assert.assertEquals(true, obj.isEvent());
+ Assert.assertEquals("2.0.0", obj.getVersion());
+ System.out.println(obj);
+ }
+
+ @Test
+ public void test_Decode_Return_Request_Event_String() throws IOException{
+ //|10011111|20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) 0xff, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ String event = Request.READONLY_EVENT;
+ byte[] request = getRequestBytes(event, header);
+
+ Request obj = (Request)decode(request);
+ Assert.assertEquals(event, obj.getData());
+ Assert.assertEquals(true, obj.isTwoWay());
+ Assert.assertEquals(true, obj.isEvent());
+ Assert.assertEquals("2.0.0", obj.getVersion());
+ System.out.println(obj);
+ }
+
+ @Test
+ public void test_Decode_Return_Request_Heartbeat_Object() throws IOException{
+ //|10011111|20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) 0xff, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ byte[] request = getRequestBytes(null, header);
+ Request obj = (Request)decode(request);
+ Assert.assertEquals(null, obj.getData());
+ Assert.assertEquals(true, obj.isTwoWay());
+ Assert.assertEquals(true, obj.isHeartbeat());
+ Assert.assertEquals("2.0.0", obj.getVersion());
+ System.out.println(obj);
+ }
+
+ @Test
+ public void test_Decode_Return_Request_Object() throws IOException{
+ //|10011111|20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) 0xdf, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Request obj = (Request)decode(request);
+ Assert.assertEquals(person, obj.getData());
+ Assert.assertEquals(true, obj.isTwoWay());
+ Assert.assertEquals(false, obj.isHeartbeat());
+ Assert.assertEquals("2.0.0", obj.getVersion());
+ System.out.println(obj);
+ }
+
+ @Test
+ public void test_Decode_Error_Request_Object() throws IOException{
+ //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte)0xdf, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+ //bad object
+ byte[] badbytes = new byte[]{-1,-2,-3,-4,-3,-4,-3,-4,-3,-4,-3,-4};
+ System.arraycopy(badbytes, 0, request, 21, badbytes.length);
+
+ Request obj = (Request)decode(request);
+ Assert.assertEquals(true, obj.isBroken());
+ Assert.assertEquals(true, obj.getData() instanceof Throwable);
+ }
+
+ @Test
+ public void test_Header_Response_NoSerializationFlag() throws IOException{
+ //00000010-response/oneway/hearbeat=false/noset |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(20, obj.getStatus());
+ Assert.assertEquals(person, obj.getResult());
+ System.out.println(obj);
+ }
+
+ @Test
+ public void test_Header_Response_Heartbeat() throws IOException{
+ //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0x20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(20, obj.getStatus());
+ Assert.assertEquals(person, obj.getResult());
+ System.out.println(obj);
+ }
+
+ @Test
+ public void test_Encode_Request() throws IOException{
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ Channel channel = getCliendSideChannel(url);
+ Request request = new Request();
+ Person person = new Person();
+ request.setData(person);
+
+ codec.encode(channel, bos, request);
+ byte[] data = bos.toByteArray();
+ bos.flush();
+ bos.close();
+
+ //encode resault check need decode
+ InputStream input = new UnsafeByteArrayInputStream(data);
+ Request obj = (Request)codec.decode(channel, input);
+ Assert.assertEquals(request.isBroken(), obj.isBroken());
+ Assert.assertEquals(request.isHeartbeat(), obj.isHeartbeat());
+ Assert.assertEquals(request.isTwoWay(), obj.isTwoWay());
+ Assert.assertEquals(person, obj.getData());
+ }
+
+ @Test
+ public void test_Encode_Response() throws IOException{
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ Channel channel = getCliendSideChannel(url);
+ Response response = new Response();
+ response.setHeartbeat(true);
+ response.setId(1001l);
+ response.setStatus((byte)20 );
+ response.setVersion("11");
+ Person person = new Person();
+ response.setResult(person);
+
+ codec.encode(channel, bos, response);
+ byte[] data = bos.toByteArray();
+ bos.flush();
+ bos.close();
+
+ //encode resault check need decode
+ InputStream input = new UnsafeByteArrayInputStream(data);
+ Response obj = (Response)codec.decode(channel, input);
+
+ Assert.assertEquals(response.getId(), obj.getId());
+ Assert.assertEquals(response.getStatus(), obj.getStatus());
+ Assert.assertEquals(response.isHeartbeat(), obj.isHeartbeat());
+ Assert.assertEquals(person, obj.getResult());
+ // encode response verson ??
+// Assert.assertEquals(response.getVersion(), obj.getVersion());
+
+ }
+
+ @Test
+ public void test_Encode_Error_Response() throws IOException{
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ Channel channel = getCliendSideChannel(url);
+ Response response = new Response();
+ response.setHeartbeat(true);
+ response.setId(1001l);
+ response.setStatus((byte)10 );
+ response.setVersion("11");
+ String badString = "bad" ;
+ response.setErrorMessage(badString);
+ Person person = new Person();
+ response.setResult(person);
+
+ codec.encode(channel, bos, response);
+ byte[] data = bos.toByteArray();
+ bos.flush();
+ bos.close();
+
+ //encode resault check need decode
+ InputStream input = new UnsafeByteArrayInputStream(data);
+ Response obj = (Response)codec.decode(channel, input);
+ Assert.assertEquals(response.getId(), obj.getId());
+ Assert.assertEquals(response.getStatus(), obj.getStatus());
+ Assert.assertEquals(response.isHeartbeat(), obj.isHeartbeat());
+ Assert.assertEquals(badString, obj.getErrorMessage());
+ Assert.assertEquals(null, obj.getResult());
+// Assert.assertEquals(response.getVersion(), obj.getVersion());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/codec/TelnetCodecTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/codec/TelnetCodecTest.java
new file mode 100644
index 0000000..1f87b4b
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java
new file mode 100644
index 0000000..2c963a1
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.remoting.exchange.support.header;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class HeartBeatTaskTest {
+
+ private URL url = URL.valueOf("dubbo://localhost:20880");
+
+ private MockChannel channel;
+ private HeartBeatTask task;
+
+ @Before
+ public void setup() throws Exception {
+ task = new HeartBeatTask( new HeartBeatTask.ChannelProvider() {
+
+ public Collection<Channel> getChannels() {
+ return Collections.<Channel>singletonList(channel);
+ }
+ }, 1000, 1000 * 3);
+
+ channel = new MockChannel() {
+
+ @Override
+ public URL getUrl() {
+ return url;
+ }
+ };
+ }
+
+ @Test
+ public void testHeartBeat() throws Exception {
+ url = url.addParameter(Constants.DUBBO_VERSION_KEY, "2.1.1");
+ channel.setAttribute(
+ HeaderExchangeHandler.KEY_READ_TIMESTAMP, System.currentTimeMillis());
+ channel.setAttribute(
+ HeaderExchangeHandler.KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
+ Thread.sleep( 2000L );
+ task.run();
+ List<Object> objects = channel.getSentObjects();
+ Assert.assertTrue(objects.size() > 0);
+ Object obj = objects.get(0);
+ Assert.assertTrue(obj instanceof Request);
+ Request request = (Request)obj;
+ Assert.assertTrue(request.isHeartbeat());
+ }
+
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/MockChannel.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/MockChannel.java
new file mode 100644
index 0000000..4f00c6b
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/exchange/support/header/MockChannel.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.remoting.exchange.support.header;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class MockChannel implements Channel {
+
+ private Map<String, Object> attributes = new HashMap<String, Object>();
+
+ private volatile boolean closed = false;
+ private List<Object> sentObjects = new ArrayList<Object>();
+
+ public InetSocketAddress getRemoteAddress() {
+ return null;
+ }
+
+ public boolean isConnected() {
+ return false;
+ }
+
+ public boolean hasAttribute(String key) {
+ return attributes.containsKey(key);
+ }
+
+ public Object getAttribute(String key) {
+ return attributes.get(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ attributes.put(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ attributes.remove(key);
+ }
+
+ public URL getUrl() {
+ return null;
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return null;
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return null;
+ }
+
+ public void send(Object message) throws RemotingException {
+ sentObjects.add(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ sentObjects.add(message);
+ }
+
+ public void close() {
+ closed = true;
+ }
+
+ public void close(int timeout) {
+ closed = true;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ public List<Object> getSentObjects() {
+ return Collections.unmodifiableList(sentObjects);
+ }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.java
new file mode 100644
index 0000000..2b90b6c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.handler;
+
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.remoting.ExecutionException;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.transport.dispather.connection.ConnectionOrderedChannelHandler;
+
+
+
+public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest{
+
+ @Before
+ public void setUp() throws Exception {
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+ }
+
+ @Test
+ public void test_Connect_Blocked() throws RemotingException{
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+ ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "connectionExecutor", 1);
+ Assert.assertEquals(1, executor.getMaximumPoolSize());
+
+ int runs = 20;
+ int taskCount = runs * 2;
+ for(int i=0; i<runs;i++){
+ handler.connected(new MockedChannel());
+ handler.disconnected(new MockedChannel());
+ Assert.assertTrue(executor.getActiveCount() + " must <=1" ,executor.getActiveCount() <= 1);
+ }
+ //queue.size
+ Assert.assertEquals(taskCount -1 , executor.getQueue().size());
+
+ for( int i=0;i<taskCount; i++){
+ if (executor.getCompletedTaskCount() < taskCount){
+ sleep(100);
+ }
+ }
+ Assert.assertEquals(taskCount, executor.getCompletedTaskCount());
+ }
+
+ @Test //biz error 不抛出到线程异常上来.
+ public void test_Connect_Biz_Error() throws RemotingException{
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+ handler.connected(new MockedChannel());
+ }
+ @Test //biz error 不抛出到线程异常上来.
+ public void test_Disconnect_Biz_Error() throws RemotingException{
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+ handler.disconnected(new MockedChannel());
+ }
+
+ @Test(expected = ExecutionException.class)
+ public void test_Connect_Execute_Error() throws RemotingException{
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+ ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "connectionExecutor", 1);
+ executor.shutdown();
+ handler.connected(new MockedChannel());
+ }
+ @Test(expected = ExecutionException.class)
+ public void test_Disconnect_Execute_Error() throws RemotingException{
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+ ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "connectionExecutor", 1);
+ executor.shutdown();
+ handler.disconnected(new MockedChannel());
+ }
+ //throw ChannelEventRunnable.runtimeExeception(int logger) not in execute exception
+ @Test//(expected = RemotingException.class)
+ public void test_MessageReceived_Biz_Error() throws RemotingException{
+ handler.received(new MockedChannel(),"");
+ }
+ //throw ChannelEventRunnable.runtimeExeception(int logger) not in execute exception
+ @Test
+ public void test_Caught_Biz_Error() throws RemotingException{
+ handler.caught(new MockedChannel(), new BizException());
+ }
+ @Test(expected = ExecutionException.class)
+ public void test_Received_InvokeInExecuter() throws RemotingException{
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+ ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "SHARED_EXECUTOR", 1);
+ executor.shutdown();
+ executor = (ThreadPoolExecutor)getField(handler, "executor", 1);
+ executor.shutdown();
+ handler.received(new MockedChannel(), "");
+ }
+
+ /**
+ * 事件不通过线程池,直接在IO上执行
+ */
+ @SuppressWarnings("deprecation")
+ @Test
+ public void test_Received_Event_invoke_direct() throws RemotingException{
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+ ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "SHARED_EXECUTOR", 1);
+ executor.shutdown();
+ executor = (ThreadPoolExecutor)getField(handler, "executor", 1);
+ executor.shutdown();
+ Request req = new Request();
+ req.setHeartbeat(true);
+ final AtomicInteger count = new AtomicInteger(0);
+ handler.received(new MockedChannel(){
+ @Override
+ public void send(Object message) throws RemotingException {
+ Assert.assertEquals("response.heartbeat", true, ((Response)message).isHeartbeat());
+ count.incrementAndGet();
+ }
+ }, req);
+ Assert.assertEquals("channel.send must be invoke", 1, count.get());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/HeaderExchangeHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/HeaderExchangeHandlerTest.java
new file mode 100644
index 0000000..e61f282
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannel.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannel.java
new file mode 100644
index 0000000..512d575
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannelHandler.java
new file mode 100644
index 0000000..08744da
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/WrappedChannelHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/WrappedChannelHandlerTest.java
new file mode 100644
index 0000000..dc981a4
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/com/alibaba/dubbo/remoting/handler/WrappedChannelHandlerTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.handler;
+
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Field;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;
+
+public class WrappedChannelHandlerTest {
+ WrappedChannelHandler handler ;
+ URL url = URL.valueOf("test://10.20.30.40:1234");
+
+ @Before
+ public void setUp() throws Exception {
+ handler = new WrappedChannelHandler(new BizChannelHander(true), url);
+ }
+
+ @Test
+ public void test_Execute_Error() throws RemotingException{
+
+ }
+
+
+ protected Object getField(Object obj, String fieldName, int parentdepth){
+ try{
+ Class<?> clazz = obj.getClass();
+ Field field = null;
+ for(int i=0;i <= parentdepth && field == null ;i++){
+ Field[] fields = clazz.getDeclaredFields();
+ for(Field f : fields){
+ if (f.getName().equals(fieldName)){
+ field = f;
+ break;
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+ if (field != null){
+ field.setAccessible(true);
+ return field.get(obj);
+ }else {
+ throw new NoSuchFieldException();
+ }
+ }catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ protected void sleep(int ms){
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ class BizChannelHander extends MockedChannelHandler{
+ private boolean invokeWithBizError;
+
+ public BizChannelHander(boolean invokeWithBizError) {
+ super();
+ this.invokeWithBizError = invokeWithBizError;
+ }
+ public BizChannelHander() {
+ super();
+ }
+
+ @Override
+ public void connected(Channel channel) throws RemotingException {
+ if (invokeWithBizError){
+ throw new RemotingException(channel, "test connect biz error");
+ }
+ sleep(20);
+ }
+
+ @Override
+ public void disconnected(Channel channel) throws RemotingException {
+ if (invokeWithBizError){
+ throw new RemotingException(channel, "test disconnect biz error");
+ }
+ sleep(20);
+ }
+ @Override
+ public void received(Channel channel, Object message) throws RemotingException {
+ if (invokeWithBizError){
+ throw new RemotingException(channel, "test received biz error");
+ }
+ sleep(20);
+ }
+ };
+
+ class BizException extends RuntimeException{
+ private static final long serialVersionUID = -7541893754900723624L;
+ }
+
+ @Test(expected = RemotingException.class)
+ public void test_Connect_Biz_Error() throws RemotingException{
+ handler.connected(new MockedChannel());
+ }
+ @Test(expected = RemotingException.class)
+ public void test_Disconnect_Biz_Error() throws RemotingException{
+ handler.disconnected(new MockedChannel());
+ }
+ @Test(expected = RemotingException.class)
+ public void test_MessageReceived_Biz_Error() throws RemotingException{
+ handler.received(new MockedChannel(),"");
+ }
+ @Test
+ public void test_Caught_Biz_Error() throws RemotingException{
+ try{
+ handler.caught(new MockedChannel(), new BizException());
+ fail();
+ }catch (Exception e) {
+ Assert.assertEquals(BizException.class, e.getCause().getClass());
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/resources/log4j.xml b/dubbo-remoting/dubbo-remoting-api/src/test/resources/log4j.xml
new file mode 100644
index 0000000..4a01abe
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/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-remoting/dubbo-remoting-grizzly/pom.xml b/dubbo-remoting/dubbo-remoting-grizzly/pom.xml
new file mode 100644
index 0000000..9d4601d
--- /dev/null
+++ b/dubbo-remoting/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-remoting</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-remoting-grizzly</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</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/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyChannel.java b/dubbo-remoting/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/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/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyClient.java b/dubbo-remoting/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/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/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java b/dubbo-remoting/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/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/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyHandler.java b/dubbo-remoting/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/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/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyServer.java b/dubbo-remoting/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/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/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.java b/dubbo-remoting/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.java
new file mode 100644
index 0000000..5f66322
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.grizzly;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.Transporter;
+
+/**
+ * GrizzlyTransporter
+ *
+ * @author william.liangf
+ */
+public class GrizzlyTransporter implements Transporter {
+
+ public static final String NAME = "grizzly";
+
+ public Server bind(URL url, ChannelHandler listener) throws RemotingException {
+ return new GrizzlyServer(url, listener);
+ }
+
+ public Client connect(URL url, ChannelHandler listener) throws RemotingException {
+ return new GrizzlyClient(url, listener);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-grizzly/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting/dubbo-remoting-grizzly/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..1cd2509
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-grizzly/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+grizzly=com.alibaba.dubbo.remoting.transport.grizzly.GrizzlyTransporter
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-http/pom.xml b/dubbo-remoting/dubbo-remoting-http/pom.xml
new file mode 100644
index 0000000..90bf5bd
--- /dev/null
+++ b/dubbo-remoting/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-remoting</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-remoting-http</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java
new file mode 100644
index 0000000..124ff26
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * HttpBinder
+ *
+ * @author william.liangf
+ */
+@SPI("jetty")
+public interface HttpBinder {
+
+ /**
+ * bind the server.
+ *
+ * @param url server url.
+ * @return server.
+ */
+ @Adaptive({Constants.SERVER_KEY})
+ HttpServer bind(URL url, HttpHandler handler);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpHandler.java b/dubbo-remoting/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/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/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpServer.java b/dubbo-remoting/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/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/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.java
new file mode 100644
index 0000000..4741b90
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http.jetty;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.http.HttpBinder;
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+import com.alibaba.dubbo.remoting.http.HttpServer;
+
+/**
+ * JettyHttpTransporter
+ *
+ * @author william.liangf
+ */
+public class JettyHttpBinder implements HttpBinder {
+
+ public HttpServer bind(URL url, HttpHandler handler) {
+ return new JettyHttpServer(url, handler);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java b/dubbo-remoting/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/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/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/DispatcherServlet.java b/dubbo-remoting/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/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/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.java
new file mode 100644
index 0000000..71d71f8
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http.servlet;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.remoting.http.HttpBinder;
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+import com.alibaba.dubbo.remoting.http.HttpServer;
+
+/**
+ * ServletHttpTransporter
+ *
+ * @author william.liangf
+ */
+public class ServletHttpBinder implements HttpBinder {
+
+ @Adaptive()
+ public HttpServer bind(URL url, HttpHandler handler) {
+ return new ServletHttpServer(url, handler);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpServer.java b/dubbo-remoting/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/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/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/support/AbstractHttpServer.java b/dubbo-remoting/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/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/dubbo-remoting-http/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.http.HttpBinder b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.http.HttpBinder
new file mode 100644
index 0000000..b1be107
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.http.HttpBinder
@@ -0,0 +1,2 @@
+servlet=com.alibaba.dubbo.remoting.http.servlet.ServletHttpBinder
+jetty=com.alibaba.dubbo.remoting.http.jetty.JettyHttpBinder
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-mina/pom.xml b/dubbo-remoting/dubbo-remoting-mina/pom.xml
new file mode 100644
index 0000000..ce8ac45
--- /dev/null
+++ b/dubbo-remoting/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-remoting</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-remoting-mina</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</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/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaChannel.java b/dubbo-remoting/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/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/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaClient.java b/dubbo-remoting/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/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/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaCodecAdapter.java b/dubbo-remoting/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/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/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaHandler.java b/dubbo-remoting/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/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/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaServer.java b/dubbo-remoting/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaServer.java
new file mode 100644
index 0000000..556af85
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaServer.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.mina;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Executors;
+
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.ThreadModel;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.transport.socket.nio.SocketAcceptor;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.AbstractServer;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelHandlers;
+
+/**
+ * MinaServer
+ *
+ * @author qian.lei
+ * @author william.liangf
+ * @author ding.lid
+ */
+public class MinaServer extends AbstractServer {
+
+ private static final Logger logger = LoggerFactory.getLogger(MinaServer.class);
+
+ private SocketAcceptor acceptor;
+
+ public MinaServer(URL url, ChannelHandler handler) throws RemotingException{
+ super(url, ChannelHandlers.wrap(handler, url.addParameterIfAbsent(Constants.THREAD_NAME_KEY, SERVER_THREAD_POOL_NAME)));
+ }
+
+ @Override
+ protected void doOpen() throws Throwable {
+ // set thread pool.
+ acceptor = new SocketAcceptor(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
+ Executors.newCachedThreadPool(new NamedThreadFactory("MinaServerWorker",
+ true)));
+ // config
+ SocketAcceptorConfig cfg = (SocketAcceptorConfig) acceptor.getDefaultConfig();
+ cfg.setThreadModel(ThreadModel.MANUAL);
+ // set codec.
+ acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MinaCodecAdapter(getCodec(), getDownstreamCodec(), getUrl(), this)));
+
+ acceptor.bind(getBindAddress(), new MinaHandler(getUrl(), this));
+ }
+
+ @Override
+ protected void doClose() throws Throwable {
+ try {
+ if (acceptor != null) {
+ acceptor.unbind(getBindAddress());
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public Collection<Channel> getChannels() {
+ Set<IoSession> sessions = acceptor.getManagedSessions(getBindAddress());
+ Collection<Channel> channels = new HashSet<Channel>();
+ for (IoSession session : sessions) {
+ if (session.isConnected()) {
+ channels.add(MinaChannel.getOrAddChannel(session, getUrl(), this));
+ }
+ }
+ return channels;
+ }
+
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ Set<IoSession> sessions = acceptor.getManagedSessions(getBindAddress());
+ for (IoSession session : sessions) {
+ if (session.getRemoteAddress().equals(remoteAddress)) {
+ return MinaChannel.getOrAddChannel(session, getUrl(), this);
+ }
+ }
+ return null;
+ }
+
+ public boolean isBound() {
+ return acceptor.isManaged(getBindAddress());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.java b/dubbo-remoting/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.java
new file mode 100644
index 0000000..534f7f3
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.mina;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.Transporter;
+
+/**
+ * @author ding.lid
+ */
+public class MinaTransporter implements Transporter {
+
+ public static final String NAME = "mina";
+
+ public Server bind(URL url, ChannelHandler handler) throws RemotingException {
+ return new MinaServer(url, handler);
+ }
+
+ public Client connect(URL url, ChannelHandler handler) throws RemotingException {
+ return new MinaClient(url, handler);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-mina/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting/dubbo-remoting-mina/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..237b77c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-mina/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+mina=com.alibaba.dubbo.remoting.transport.mina.MinaTransporter
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientToServerTest.java b/dubbo-remoting/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/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/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientsTest.java b/dubbo-remoting/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientsTest.java
new file mode 100644
index 0000000..98de2ea
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.remoting.transport.mina;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.Transporter;
+import com.alibaba.dubbo.remoting.transport.mina.MinaTransporter;
+
+/**
+ * @author ding.lid
+ */
+public class ClientsTest {
+
+ @Test
+ public void testGetTransportEmpty() {
+ try {
+ ExtensionLoader.getExtensionLoader(Transporter.class).getExtension("");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(), containsString("Extension name == null"));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetTransportNull() {
+ String name = null;
+ ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name);
+ }
+
+ @Test
+ public void testGetTransport1() {
+ String name = "mina";
+ assertEquals(MinaTransporter.class, ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetTransportWrong() {
+ String name = "nety";
+ assertNull(ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/Hello.java b/dubbo-remoting/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/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/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/MinaClientToServerTest.java b/dubbo-remoting/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/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/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/World.java b/dubbo-remoting/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/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/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/WorldHandler.java b/dubbo-remoting/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/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/dubbo-remoting-netty/pom.xml b/dubbo-remoting/dubbo-remoting-netty/pom.xml
new file mode 100644
index 0000000..9817309
--- /dev/null
+++ b/dubbo-remoting/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-remoting</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</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/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyChannel.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyClient.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyCodecAdapter.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyHandler.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyServer.java b/dubbo-remoting/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyServer.java
new file mode 100644
index 0000000..12359f1
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyServer.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelFactory;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.transport.AbstractServer;
+import com.alibaba.dubbo.remoting.transport.dispather.ChannelHandlers;
+
+/**
+ * NettyServer
+ *
+ * @author qian.lei
+ * @author chao.liuc
+ */
+public class NettyServer extends AbstractServer implements Server {
+
+ private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
+
+ private Map<String, Channel> channels; // <ip:port, channel>
+
+ private ServerBootstrap bootstrap;
+
+ private org.jboss.netty.channel.Channel channel;
+
+ public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
+ super(url, ChannelHandlers.wrap(handler, url.addParameterIfAbsent(Constants.THREAD_NAME_KEY, SERVER_THREAD_POOL_NAME)));
+ }
+
+ @Override
+ protected void doOpen() throws Throwable {
+ ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
+ ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
+ ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
+ bootstrap = new ServerBootstrap(channelFactory);
+
+ final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
+ channels = nettyHandler.getChannels();
+ // https://issues.jboss.org/browse/NETTY-365
+ // https://issues.jboss.org/browse/NETTY-379
+ // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
+ bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
+ public ChannelPipeline getPipeline() {
+ NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getDownstreamCodec(), getUrl(), NettyServer.this);
+ ChannelPipeline pipeline = Channels.pipeline();
+ /*int idleTimeout = getIdleTimeout();
+ if (idleTimeout > 10000) {
+ pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
+ }*/
+ pipeline.addLast("decoder", adapter.getDecoder());
+ pipeline.addLast("encoder", adapter.getEncoder());
+ pipeline.addLast("handler", nettyHandler);
+ return pipeline;
+ }
+ });
+ // bind
+ channel = bootstrap.bind(getBindAddress());
+ }
+
+ @Override
+ protected void doClose() throws Throwable {
+ try {
+ if (channel != null) {
+ // unbind.
+ channel.close();
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ Collection<com.alibaba.dubbo.remoting.Channel> channels = getChannels();
+ if (channels != null && channels.size() > 0) {
+ for (com.alibaba.dubbo.remoting.Channel channel : channels) {
+ try {
+ channel.close();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ if (bootstrap != null) {
+ // release external resource.
+ bootstrap.releaseExternalResources();
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ if (channels != null) {
+ channels.clear();
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public Collection<Channel> getChannels() {
+ Collection<Channel> chs = new HashSet<Channel>();
+ for (Channel channel : this.channels.values()) {
+ if (channel.isConnected()) {
+ chs.add(channel);
+ } else {
+ channels.remove(NetUtils.toAddressString(channel.getRemoteAddress()));
+ }
+ }
+ return chs;
+ }
+
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ return channels.get(NetUtils.toAddressString(remoteAddress));
+ }
+
+ public boolean isBound() {
+ return channel.isBound();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.java b/dubbo-remoting/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.java
new file mode 100644
index 0000000..f899a6f
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.Transporter;
+
+/**
+ * @author ding.lid
+ */
+public class NettyTransporter implements Transporter {
+
+ public static final String NAME = "netty";
+
+ public Server bind(URL url, ChannelHandler listener) throws RemotingException {
+ return new NettyServer(url, listener);
+ }
+
+ public Client connect(URL url, ChannelHandler listener) throws RemotingException {
+ return new NettyClient(url, listener);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-netty/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting/dubbo-remoting-netty/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..eb9bcb2
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+netty=com.alibaba.dubbo.remoting.transport.netty.NettyTransporter
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientReconnectTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientReconnectTest.java
new file mode 100644
index 0000000..f6ae879
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientReconnectTest.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.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);
+ }
+ }
+
+ /**
+ * 重连日志的校验,时间不够shutdown time时,不能有error日志,但必须有一条warn日志
+ */
+ @Test
+ public void testReconnectWarnLog() 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(1500);//重连线程的运行
+ //时间不够长,不会产生error日志
+ Assert.assertEquals("no error message ", 0 , LogUtil.findMessage(Level.ERROR, "client reconnect to "));
+ //第一次重连失败就会有warn日志
+ Assert.assertEquals("must have one warn message ", 1 , 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+ "=1";//shutdown时间足够短,确保error日志输出
+ try{
+ Exchangers.connect(url);
+ }catch (Exception e) {
+ //do nothing
+ }
+ Thread.sleep(1500);//重连线程的运行
+ Assert.assertEquals("only one error message ", 1 , LogUtil.findMessage(Level.ERROR, "client reconnect to "));
+ DubboAppender.doStop();
+ }
+
+ /**
+ * 测试client重连方法不会导致重连线程失效.
+ */
+ @Test
+ public void testClientReconnectMethod() throws RemotingException, InterruptedException{
+ int port = NetUtils.getAvailablePort();
+ String url = "exchange://127.0.0.3:"+port + "/client.reconnect.test?check=false&"
+ +Constants.RECONNECT_KEY+"="+10 //1ms reconnect,保证有足够频率的重连
+ +"&reconnect.waring.period=1";
+ DubboAppender.doStart();
+ Client client = Exchangers.connect(url);
+ try {
+ client.reconnect();
+ } catch (Exception e) {
+ //do nothing
+ }
+ Thread.sleep(1500);//重连线程的运行
+ Assert.assertTrue("have more then one warn msgs . bug was :" + LogUtil.findMessage(Level.WARN, "client reconnect to "),LogUtil.findMessage(Level.WARN, "client reconnect to ") >1);
+ DubboAppender.doStop();
+ }
+ public static void main(String[] args) {
+ System.out.println(3%1);
+ }
+
+ /**
+ * 重连日志的校验
+ */
+ @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/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientToServerTest.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientsTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientsTest.java
new file mode 100644
index 0000000..10aa3d7
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientsTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.Transporter;
+
+/**
+ * @author ding.lid
+ */
+public class ClientsTest {
+
+ @Test
+ public void testGetTransportEmpty() {
+ try {
+ ExtensionLoader.getExtensionLoader(Transporter.class).getExtension("");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(), containsString("Extension name == null"));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetTransportNull() {
+ String name = null;
+ ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name);
+ }
+
+ @Test
+ public void testGetTransport3() {
+ String name = "netty";
+ assertEquals(NettyTransporter.class, ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetTransportWrong() {
+ String name = "nety";
+ assertNull(ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/Hello.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.java
new file mode 100644
index 0000000..c1da6d1
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.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.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.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+
+/**
+ * User: heyman
+ * Date: 5/3/11
+ * Time: 5:47 PM
+ */
+public class NettyClientTest {
+ static Server server;
+
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ server = Exchangers.bind(URL.valueOf("exchange://localhost:10001?server=netty"), new TelnetServerHandler());
+ }
+
+ @Test
+ public void testClientClose() throws Exception {
+ List<ExchangeChannel> clients = new ArrayList<ExchangeChannel>(100);
+ for (int i = 0; i < 100; i++) {
+ ExchangeChannel client = Exchangers.connect(URL.valueOf("exchange://localhost:10001?client=netty"));
+ Thread.sleep(5);
+ clients.add(client);
+ }
+ for (ExchangeChannel client : clients){
+ client.close();
+ }
+ Thread.sleep(1000);
+ }
+
+ @Test
+ public void testServerClose() throws Exception {
+ for (int i = 0; i < 100; i++) {
+ Server aServer = Exchangers.bind(URL.valueOf("exchange://localhost:" + (5000 + i) + "?client=netty"), new TelnetServerHandler());
+ aServer.close();
+ }
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ try {
+ if (server != null)
+ server.close();
+ } finally {}
+ }
+
+ public static void main(String[] args) throws RemotingException, InterruptedException {
+ ExchangeChannel client = Exchangers.connect(URL.valueOf("exchange://10.20.153.10:20880?client=netty&heartbeat=1000"));
+ Thread.sleep(60*1000*50);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientToServerTest.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyStringTest.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetClientHandler.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetServerHandler.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/World.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/WorldHandler.java b/dubbo-remoting/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/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/dubbo-remoting-netty/src/test/resources/log4j.xml b/dubbo-remoting/dubbo-remoting-netty/src/test/resources/log4j.xml
new file mode 100644
index 0000000..8e82064
--- /dev/null
+++ b/dubbo-remoting/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/dubbo-remoting-p2p/pom.xml b/dubbo-remoting/dubbo-remoting-p2p/pom.xml
new file mode 100644
index 0000000..6d03cd7
--- /dev/null
+++ b/dubbo-remoting/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-remoting</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-remoting-p2p</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Group.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networker.java b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networker.java
new file mode 100644
index 0000000..5298348
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networker.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.SPI;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * Networker. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Peer-to-peer">Peer-to-peer</a>
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface Networker {
+
+ /**
+ * lookup group.
+ *
+ * @param url group url
+ * @return group.
+ */
+ Group lookup(URL url) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networkers.java b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networkers.java
new file mode 100644
index 0000000..3df97f7
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Networkers.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * Networkers. (API, Static, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Peer-to-peer">Peer-to-peer</a>
+ *
+ * @author william.liangf
+ */
+public class Networkers {
+
+ public static Peer join(String group, String peer, ChannelHandler handler) throws RemotingException {
+ return join(URL.valueOf(group), URL.valueOf(peer), handler);
+ }
+
+ public static Peer join(URL group, URL peer, ChannelHandler handler) throws RemotingException {
+ return lookup(group).join(peer, handler);
+ }
+
+ public static Group lookup(String group) throws RemotingException {
+ return lookup(URL.valueOf(group));
+ }
+
+ public static Group lookup(URL group) throws RemotingException {
+ Networker networker = ExtensionLoader.getExtensionLoader(Networker.class).getExtension(group.getProtocol());
+ return networker.lookup(group);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/Peer.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeGroup.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworker.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworkers.java b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworkers.java
new file mode 100644
index 0000000..fcb0f3a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworkers.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.exchange;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+
+/**
+ * Networkers
+ *
+ * @author william.liangf
+ */
+public class ExchangeNetworkers {
+
+ public static ExchangePeer join(String group, String peer, ExchangeHandler handler) throws RemotingException {
+ return join(URL.valueOf(group), URL.valueOf(peer), handler);
+ }
+
+ public static ExchangePeer join(URL group, URL peer, ExchangeHandler handler) throws RemotingException {
+ return lookup(group).join(peer, handler);
+ }
+
+ public static ExchangeGroup lookup(String group) throws RemotingException {
+ return lookup(URL.valueOf(group));
+ }
+
+ public static ExchangeGroup lookup(URL group) throws RemotingException {
+ ExchangeNetworker networker = ExtensionLoader.getExtensionLoader(ExchangeNetworker.class).getExtension(group.getProtocol());
+ return networker.lookup(group);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangePeer.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/AbstractExchangeGroup.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/ExchangeServerPeer.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeGroup.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.java b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.java
new file mode 100644
index 0000000..85d7a1b
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.exchange.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeGroup;
+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeNetworker;
+
+/**
+ * FileNetworker
+ *
+ * @author william.liangf
+ */
+public class FileExchangeNetworker implements ExchangeNetworker {
+
+ public ExchangeGroup lookup(URL url) throws RemotingException {
+ return new FileExchangeGroup(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.java b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.java
new file mode 100644
index 0000000..f2345cd
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.exchange.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeGroup;
+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeNetworker;
+
+/**
+ * MulticastNetworker
+ *
+ * @author william.liangf
+ */
+public class MulticastExchangeNetworker implements ExchangeNetworker {
+
+ public ExchangeGroup lookup(URL url) throws RemotingException {
+ return new MulticastExchangeGroup(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/AbstractGroup.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileGroup.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.java b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.java
new file mode 100644
index 0000000..1aa4f88
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.p2p.Group;
+import com.alibaba.dubbo.remoting.p2p.Networker;
+
+/**
+ * FileNetworker
+ *
+ * @author william.liangf
+ */
+public class FileNetworker implements Networker {
+
+ public Group lookup(URL url) throws RemotingException {
+ return new FileGroup(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastGroup.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.java b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.java
new file mode 100644
index 0000000..4699f77
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.p2p.Group;
+import com.alibaba.dubbo.remoting.p2p.Networker;
+
+/**
+ * MulticastNetworker
+ *
+ * @author william.liangf
+ */
+public class MulticastNetworker implements Networker {
+
+ public Group lookup(URL url) throws RemotingException {
+ return new MulticastGroup(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/com/alibaba/dubbo/remoting/p2p/support/ServerPeer.java b/dubbo-remoting/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/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/dubbo-remoting-p2p/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker b/dubbo-remoting/dubbo-remoting-p2p/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker
new file mode 100644
index 0000000..521c6c0
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker
@@ -0,0 +1,2 @@
+multicast=com.alibaba.dubbo.remoting.p2p.support.MulticastNetworker
+file=com.alibaba.dubbo.remoting.p2p.support.FileNetworker
\ No newline at end of file
diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/test/java/com/alibaba/dubbo/remoting/p2p/PeerMain.java b/dubbo-remoting/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/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..04aefe8
--- /dev/null
+++ b/dubbo-remoting/pom.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.
+-->
+<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.2.2</version>
+ </parent>
+ <artifactId>dubbo-remoting</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The remoting module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-remoting-api</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>
+ </modules>
+</project>
diff --git a/dubbo-rpc/dubbo-rpc-api/pom.xml b/dubbo-rpc/dubbo-rpc-api/pom.xml
new file mode 100644
index 0000000..9d65aad
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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-rpc</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-rpc-api</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Exporter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Exporter.java
new file mode 100644
index 0000000..1494390
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.java
new file mode 100644
index 0000000..7c9ec82
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * ExporterListener. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface ExporterListener {
+
+ /**
+ * The exporter exported.
+ *
+ * @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)
+ * @param exporter
+ * @throws RpcException
+ */
+ void exported(Exporter<?> exporter) throws RpcException;
+
+ /**
+ * The exporter unexported.
+ *
+ * @see com.alibaba.dubbo.rpc.Exporter#unexport()
+ * @param exporter
+ * @throws RpcException
+ */
+ void unexported(Exporter<?> exporter);
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Filter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Filter.java
new file mode 100644
index 0000000..e57d6c7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Filter.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * Filter. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface Filter {
+
+ /**
+ * do invoke filter.
+ *
+ * <code>
+ * // before filter
+ * Result result = invoker.invoke(invocation);
+ * // after filter
+ * return result;
+ * </code>
+ *
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @param invoker service
+ * @param invocation invocation.
+ * @return invoke result.
+ * @throws RpcException
+ */
+ Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Invocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
new file mode 100644
index 0000000..94ca82d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.util.Map;
+
+/**
+ * Invocation. (API, Prototype, NonThreadSafe)
+ *
+ * @serial Don't change the class name and package name.
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @see com.alibaba.dubbo.rpc.RpcInvocation
+ * @author qian.lei
+ * @author william.liangf
+ */
+public interface Invocation {
+
+ /**
+ * get method name.
+ *
+ * @serial
+ * @return method name.
+ */
+ String getMethodName();
+
+ /**
+ * get parameter types.
+ *
+ * @serial
+ * @return parameter types.
+ */
+ Class<?>[] getParameterTypes();
+
+ /**
+ * get arguments.
+ *
+ * @serial
+ * @return arguments.
+ */
+ Object[] getArguments();
+
+ /**
+ * get attachments.
+ *
+ * @serial
+ * @return attachments.
+ */
+ Map<String, String> getAttachments();
+
+ /**
+ * get attachment by key.
+ *
+ * @serial
+ * @return attachment value.
+ */
+ String getAttachment(String key);
+
+ /**
+ * get attachment by key with default value.
+ *
+ * @serial
+ * @return attachment value.
+ */
+ String getAttachment(String key, String defaultValue);
+
+ /**
+ * get the invoker in current context.
+ *
+ * @transient
+ * @return invoker.
+ */
+ Invoker<?> getInvoker();
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Invoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Invoker.java
new file mode 100644
index 0000000..d675bc1
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Invoker.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.Node;
+
+/**
+ * Invoker. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, com.alibaba.dubbo.common.URL)
+ * @see com.alibaba.dubbo.rpc.InvokerListener
+ * @see com.alibaba.dubbo.rpc.protocol.AbstractInvoker
+ * @author william.liangf
+ */
+public interface Invoker<T> extends Node {
+
+ /**
+ * get service interface.
+ *
+ * @return service interface.
+ */
+ Class<T> getInterface();
+
+ /**
+ * invoke.
+ *
+ * @param invocation
+ * @return result
+ * @throws RpcException
+ */
+ Result invoke(Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java
new file mode 100644
index 0000000..1200b6a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * InvokerListener. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@SPI
+public interface InvokerListener {
+
+ /**
+ * The invoker referred
+ *
+ * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, URL)
+ * @param invoker
+ * @throws RpcException
+ */
+ void referred(Invoker<?> invoker) throws RpcException;
+
+ /**
+ * The invoker destroyed.
+ *
+ * @see com.alibaba.dubbo.rpc.Invoker#destroy()
+ * @param invoker
+ */
+ void destroyed(Invoker<?> invoker);
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Protocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Protocol.java
new file mode 100644
index 0000000..8da6bd8
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Protocol.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;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * Protocol. (API/SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@SPI("dubbo")
+public interface Protocol {
+
+ /**
+ * 获取缺省端口,当用户没有配置端口时使用。
+ *
+ * @return 缺省端口
+ */
+ int getDefaultPort();
+
+ /**
+ * 暴露远程服务:<br>
+ * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
+ * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
+ * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>
+ *
+ * @param <T> 服务的类型
+ * @param invoker 服务的执行体
+ * @return exporter 暴露服务的引用,用于取消暴露
+ * @throws RpcException 当暴露服务出错时抛出,比如端口已占用
+ */
+ @Adaptive
+ <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
+
+ /**
+ * 引用远程服务:<br>
+ * 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br>
+ * 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。<br>
+ * 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br>
+ *
+ * @param <T> 服务的类型
+ * @param type 服务的类型
+ * @param url 远程服务的URL地址
+ * @return invoker 服务的本地代理
+ * @throws RpcException 当连接服务提供方失败时抛出
+ */
+ @Adaptive
+ <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
+
+ /**
+ * 释放协议:<br>
+ * 1. 取消该协议所有已经暴露和引用的服务。<br>
+ * 2. 释放协议所占用的所有资源,比如连接和端口。<br>
+ * 3. 协议在释放后,依然能暴露和引用新的服务。<br>
+ */
+ void destroy();
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java
new file mode 100644
index 0000000..2cc798d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Adaptive;
+import com.alibaba.dubbo.common.extension.SPI;
+
+/**
+ * ProxyFactory. (API/SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@SPI("javassist")
+public interface ProxyFactory {
+
+ /**
+ * create proxy.
+ *
+ * @param invoker
+ * @return proxy
+ */
+ @Adaptive({Constants.PROXY_KEY})
+ <T> T getProxy(Invoker<T> invoker) throws RpcException;
+
+ /**
+ * create invoker.
+ *
+ * @param <T>
+ * @param proxy
+ * @param type
+ * @param url
+ * @return invoker
+ */
+ @Adaptive({Constants.PROXY_KEY})
+ <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Result.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Result.java
new file mode 100644
index 0000000..174ab9d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/Result.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+/**
+ * RPC invoke result. (API, Prototype, NonThreadSafe)
+ *
+ * @serial Don't change the class name and package name.
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @see com.alibaba.dubbo.rpc.RpcResult
+ * @author qianlei
+ * @author william.liangf
+ */
+public interface Result {
+
+ /**
+ * Get invoke result.
+ *
+ * @return result. if no result return null.
+ */
+ Object getValue();
+
+ /**
+ * Get exception.
+ *
+ * @return exception. if no exception return null.
+ */
+ Throwable getException();
+
+ /**
+ * Has exception.
+ *
+ * @return has exception.
+ */
+ boolean hasException();
+
+ /**
+ * Recreate.
+ *
+ * <code>
+ * if (hasException()) {
+ * throw getException();
+ * } else {
+ * return getValue();
+ * }
+ * </code>
+ *
+ * @return result.
+ * @throws if has exception throw it.
+ */
+ Object recreate() throws Throwable;
+
+ /**
+ * @deprecated Replace to getValue()
+ * @see com.alibaba.dubbo.rpc.Result#getValue()
+ */
+ @Deprecated
+ Object getResult();
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java
new file mode 100644
index 0000000..c1275d5
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.Constants;
+
+/**
+ * RpcConstants
+ *
+ * @deprecated Replace to com.alibaba.dubbo.common.Constants
+ * @author william.liangf
+ */
+@Deprecated
+public final class RpcConstants extends Constants {
+
+ private RpcConstants() {}
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java
new file mode 100644
index 0000000..eaf72c2
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java
@@ -0,0 +1,624 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+
+/**
+ * Thread local context. (API, ThreadLocal, ThreadSafe)
+ *
+ * 注意:RpcContext是一个临时状态记录器,当接收到RPC请求,或发起RPC请求时,RpcContext的状态都会变化。
+ * 比如:A调B,B再调C,则B机器上,在B调C之前,RpcContext记录的是A调B的信息,在B调C之后,RpcContext记录的是B调C的信息。
+ *
+ * @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 Future<?> future;
+
+ private List<URL> urls;
+
+ private URL url;
+
+ private String methodName;
+
+ private Class<?>[] parameterTypes;
+
+ private Object[] arguments;
+
+ private InetSocketAddress localAddress;
+
+ private InetSocketAddress remoteAddress;
+
+ private final Map<String, String> attachments = new HashMap<String, String>();
+
+ private final Map<String, Object> values = new HashMap<String, Object>();
+
+ @Deprecated
+ private List<Invoker<?>> invokers;
+
+ @Deprecated
+ private Invoker<?> invoker;
+
+ @Deprecated
+ private Invocation invocation;
+
+ protected RpcContext() {
+ }
+
+ /**
+ * is provider side.
+ *
+ * @return provider side.
+ */
+ public boolean isProviderSide() {
+ URL url = 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));
+ }
+
+ /**
+ * is consumer side.
+ *
+ * @return consumer side.
+ */
+ public boolean isConsumerSide() {
+ URL url = 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));
+ }
+
+ /**
+ * 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;
+ }
+
+ public List<URL> getUrls() {
+ return urls == null && url != null ? (List<URL>) Arrays.asList(url) : urls;
+ }
+
+ public void setUrls(List<URL> urls) {
+ this.urls = urls;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ /**
+ * get method name.
+ *
+ * @return method name.
+ */
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ /**
+ * get parameter types.
+ *
+ * @serial
+ */
+ public Class<?>[] getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public void setParameterTypes(Class<?>[] parameterTypes) {
+ this.parameterTypes = parameterTypes;
+ }
+
+ /**
+ * get arguments.
+ *
+ * @return arguments.
+ */
+ public Object[] getArguments() {
+ return arguments;
+ }
+
+ public void setArguments(Object[] arguments) {
+ this.arguments = arguments;
+ }
+
+ /**
+ * 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 attachment.
+ *
+ * @param key
+ * @return attachment
+ */
+ public String getAttachment(String key) {
+ return attachments.get(key);
+ }
+
+ /**
+ * 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 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;
+ }
+
+ public void clearAttachments() {
+ this.attachments.clear();
+ }
+
+ /**
+ * get values.
+ *
+ * @return values
+ */
+ public Map<String, Object> get() {
+ return values;
+ }
+
+ /**
+ * 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);
+ }
+
+ public RpcContext setInvokers(List<Invoker<?>> invokers) {
+ this.invokers = invokers;
+ if (invokers != null && invokers.size() > 0) {
+ List<URL> urls = new ArrayList<URL>(invokers.size());
+ for (Invoker<?> invoker : invokers) {
+ urls.add(invoker.getUrl());
+ }
+ setUrls(urls);
+ }
+ return this;
+ }
+
+ public RpcContext setInvoker(Invoker<?> invoker) {
+ this.invoker = invoker;
+ if (invoker != null) {
+ setUrl(invoker.getUrl());
+ }
+ return this;
+ }
+
+ public RpcContext setInvocation(Invocation invocation) {
+ this.invocation = invocation;
+ if (invocation != null) {
+ setMethodName(invocation.getMethodName());
+ setParameterTypes(invocation.getParameterTypes());
+ setArguments(invocation.getArguments());
+ }
+ return this;
+ }
+
+ /**
+ * @deprecated Replace to isProviderSide()
+ */
+ @Deprecated
+ public boolean isServerSide() {
+ return isProviderSide();
+ }
+
+ /**
+ * @deprecated Replace to isConsumerSide()
+ */
+ @Deprecated
+ public boolean isClientSide() {
+ return isConsumerSide();
+ }
+
+ /**
+ * @deprecated Replace to getUrls()
+ */
+ @Deprecated
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public List<Invoker<?>> getInvokers() {
+ return invokers == null && invoker != null ? (List)Arrays.asList(invoker) : invokers;
+ }
+
+ /**
+ * @deprecated Replace to getUrl()
+ */
+ @Deprecated
+ public Invoker<?> getInvoker() {
+ return invoker;
+ }
+
+ /**
+ * @deprecated Replace to getMethodName(), getParameterTypes(), getArguments()
+ */
+ @Deprecated
+ public Invocation getInvocation() {
+ return invocation;
+ }
+
+ /**
+ * 异步调用 ,需要返回值,即使步调用Future.get方法,也会处理调用超时问题.
+ * @param callable
+ * @return 通过future.get()获取返回结果.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> Future<T> asyncCall(Callable<T> callable) {
+ try {
+ try {
+ setAttachment(Constants.Attachments.IS_ASYNC_KEY, Boolean.TRUE.toString());
+ final T o = callable.call();
+ //local调用会直接返回结果.
+ if (o != null) {
+ FutureTask<T> f = new FutureTask<T>(new Callable<T>() {
+ public T call() throws Exception {
+ return o;
+ }
+ });
+ f.run();
+ return f;
+ } else {
+
+ }
+ } catch (Exception e) {
+ throw new RpcException(e);
+ } finally {
+ removeAttachment(Constants.Attachments.IS_ASYNC_KEY);
+ }
+ } catch (final RpcException e) {
+ return new Future<T>() {
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+ public boolean isCancelled() {
+ return false;
+ }
+ public boolean isDone() {
+ return true;
+ }
+ public T get() throws InterruptedException, ExecutionException {
+ throw new ExecutionException(e.getCause());
+ }
+ public T get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException,
+ TimeoutException {
+ return get();
+ }
+ };
+ }
+ return ((Future<T>)getContext().getFuture());
+ }
+
+ /**
+ * oneway调用,只发送请求,不接收返回结果.
+ * @param callable
+ */
+ public void asyncCall(Runnable runable) {
+ try {
+ setAttachment(Constants.Attachments.IS_ONEWAY_KEY, Boolean.TRUE.toString());
+ runable.run();
+ } catch (Throwable e) {
+ //FIXME 异常是否应该放在future中?
+ throw new RpcException("oneway call error ." + e.getMessage(), e);
+ } finally {
+ removeAttachment(Constants.Attachments.IS_ONEWAY_KEY);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcException.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcException.java
new file mode 100644
index 0000000..1ae2295
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcException.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+/**
+ * RPC Exception. (API, Prototype, ThreadSafe)
+ *
+ * @serial Don't change the class name and properties.
+ * @since 1.0
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @author shawn.qianx
+ * @author william.liangf
+ */
+public final class RpcException extends RuntimeException {
+
+ private static final long serialVersionUID = 7815426752583648734L;
+
+ public static final int UNKNOWN_EXCEPTION = 0;
+
+ public static final int NETWORK_EXCEPTION = 1;
+
+ public static final int TIMEOUT_EXCEPTION = 2;
+
+ public static final int BIZ_EXCEPTION = 3;
+
+ public static final int FORBIDDEN_EXCEPTION = 4;
+
+ public static final int SERIALIZATION_EXCEPTION = 5;
+
+ private int code; // RpcException不能有子类,异常类型用ErrorCode表示,以便保持兼容。
+
+ public RpcException() {
+ super();
+ }
+
+ public RpcException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public RpcException(String message) {
+ super(message);
+ }
+
+ public RpcException(Throwable cause) {
+ super(cause);
+ }
+
+ public RpcException(int code) {
+ super();
+ this.code = code;
+ }
+
+ public RpcException(int code, String message, Throwable cause) {
+ super(message, cause);
+ this.code = code;
+ }
+
+ public RpcException(int code, String message) {
+ super(message);
+ this.code = code;
+ }
+
+ public RpcException(int code, Throwable cause) {
+ super(cause);
+ this.code = code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public boolean isBiz() {
+ return code == BIZ_EXCEPTION;
+ }
+
+ public boolean isForbidded() {
+ return code == FORBIDDEN_EXCEPTION;
+ }
+
+ public boolean isTimeout() {
+ return code == TIMEOUT_EXCEPTION;
+ }
+
+ public boolean isNetwork() {
+ return code == NETWORK_EXCEPTION;
+ }
+
+ public boolean isSerialization() {
+ return code == SERIALIZATION_EXCEPTION;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java
new file mode 100644
index 0000000..54fe5cd
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * RPC Invocation.
+ *
+ * @serial Don't change the class name and properties.
+ * @author qian.lei
+ */
+public class RpcInvocation implements Invocation, Serializable {
+
+ private static final long serialVersionUID = -4355285085441097045L;
+
+ private String methodName;
+
+ private Class<?>[] parameterTypes;
+
+ private Object[] arguments;
+
+ private Map<String, String> attachments;
+
+ private transient Invoker<?> invoker;
+
+ public RpcInvocation() {
+ }
+
+ public RpcInvocation(Invocation invocation, Invoker<?> invoker) {
+ this(invocation.getMethodName(), invocation.getParameterTypes(),
+ invocation.getArguments(), new HashMap<String, String>(invocation.getAttachments()),
+ invocation.getInvoker());
+ if (invoker != null) {
+ URL url = invoker.getUrl();
+ setAttachment(Constants.PATH_KEY, url.getPath());
+ if (url.hasParameter(Constants.INTERFACE_KEY)) {
+ setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));
+ }
+ if (url.hasParameter(Constants.GROUP_KEY)) {
+ setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));
+ }
+ if (url.hasParameter(Constants.VERSION_KEY)) {
+ setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY, "0.0.0"));
+ }
+ if (url.hasParameter(Constants.TIMEOUT_KEY)) {
+ setAttachment(Constants.TIMEOUT_KEY, url.getParameter(Constants.TIMEOUT_KEY));
+ }
+ if (url.hasParameter(Constants.TOKEN_KEY)) {
+ setAttachment(Constants.TOKEN_KEY, url.getParameter(Constants.TOKEN_KEY));
+ }
+ if (url.hasParameter(Constants.APPLICATION_KEY)) {
+ setAttachment(Constants.APPLICATION_KEY, url.getParameter(Constants.APPLICATION_KEY));
+ }
+ }
+ }
+
+ public RpcInvocation(Invocation invocation) {
+ this(invocation.getMethodName(), invocation.getParameterTypes(),
+ invocation.getArguments(), invocation.getAttachments(), invocation.getInvoker());
+ }
+
+ public RpcInvocation(Method method, Object[] arguments) {
+ this(method.getName(), method.getParameterTypes(), arguments, null, null);
+ }
+
+ public RpcInvocation(Method method, Object[] arguments, Map<String, String> attachment) {
+ this(method.getName(), method.getParameterTypes(), arguments, attachment, null);
+ }
+
+ public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments) {
+ this(methodName, parameterTypes, arguments, null, null);
+ }
+
+ public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments, Map<String, String> attachments) {
+ this(methodName, parameterTypes, arguments, attachments, null);
+ }
+
+ public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments, Map<String, String> attachments, Invoker<?> invoker) {
+ this.methodName = methodName;
+ this.parameterTypes = parameterTypes == null ? new Class<?>[0] : parameterTypes;
+ this.arguments = arguments == null ? new Object[0] : arguments;
+ this.attachments = attachments == null ? new HashMap<String, String>() : attachments;
+ this.invoker = invoker;
+ }
+
+ public Invoker<?> getInvoker() {
+ return invoker;
+ }
+
+ public void setInvoker(Invoker<?> invoker) {
+ this.invoker = invoker;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public Object[] getArguments() {
+ return arguments;
+ }
+
+ public Map<String, String> getAttachments() {
+ return attachments;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public void setParameterTypes(Class<?>[] parameterTypes) {
+ this.parameterTypes = parameterTypes == null ? new Class<?>[0] : parameterTypes;
+ }
+
+ public void setArguments(Object[] arguments) {
+ this.arguments = arguments == null ? new Object[0] : arguments;
+ }
+
+ public void setAttachments(Map<String, String> attachments) {
+ this.attachments = attachments == null ? new HashMap<String, String>() : attachments;
+ }
+
+ public void setAttachment(String key, String value) {
+ if (attachments == null) {
+ attachments = new HashMap<String, String>();
+ }
+ attachments.put(key, value);
+ }
+
+ public String getAttachment(String key) {
+ if (attachments == null) {
+ return null;
+ }
+ return attachments.get(key);
+ }
+
+ public String getAttachment(String key, String defaultValue) {
+ if (attachments == null) {
+ return defaultValue;
+ }
+ String value = attachments.get(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "RpcInvocation [methodName=" + methodName + ", parameterTypes="
+ + Arrays.toString(parameterTypes) + ", arguments=" + Arrays.toString(arguments)
+ + ", attachments=" + attachments + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcResult.java
new file mode 100644
index 0000000..3de0d08
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcResult.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.io.Serializable;
+
+/**
+ * RPC Result.
+ *
+ * @serial Don't change the class name and properties.
+ * @author qianlei
+ */
+public class RpcResult implements Result, Serializable {
+
+ private static final long serialVersionUID = -6925924956850004727L;
+
+ private Object result;
+
+ private Throwable exception;
+
+ public RpcResult(){
+ }
+
+ public RpcResult(Object result){
+ this.result = result;
+ }
+
+ public RpcResult(Throwable exception){
+ this.exception = exception;
+ }
+
+ public Object recreate() throws Throwable {
+ if (exception != null) {
+ throw exception;
+ }
+ return result;
+ }
+
+ /**
+ * @deprecated Replace to getValue()
+ * @see com.alibaba.dubbo.rpc.RpcResult#getValue()
+ */
+ @Deprecated
+ public Object getResult() {
+ return getValue();
+ }
+
+ /**
+ * @deprecated Replace to setValue()
+ * @see com.alibaba.dubbo.rpc.RpcResult#setValue()
+ */
+ @Deprecated
+ public void setResult(Object result) {
+ setValue(result);
+ }
+
+ public Object getValue() {
+ return result;
+ }
+
+ public void setValue(Object value) {
+ this.result = value;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public void setException(Throwable e) {
+ this.exception = e;
+ }
+
+ public boolean hasException() {
+ return exception != null;
+ }
+
+ @Override
+ public String toString() {
+ return "RpcResult [result=" + result + ", exception=" + exception + "]";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java
new file mode 100644
index 0000000..6f51b60
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * URL statistics. (API, Cached, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
+ * @see com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
+ * @see com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
+ * @author william.liangf
+ */
+public class RpcStatus {
+
+ private static final ConcurrentMap<String, RpcStatus> SERVICE_STATISTICS = new ConcurrentHashMap<String, RpcStatus>();
+
+ private static final ConcurrentMap<String, ConcurrentMap<String, RpcStatus>> METHOD_STATISTICS = new ConcurrentHashMap<String, ConcurrentMap<String, RpcStatus>>();
+
+ /**
+ *
+ * @param url
+ * @return status
+ */
+ public static RpcStatus getStatus(URL url) {
+ String uri = url.toIdentityString();
+ RpcStatus status = SERVICE_STATISTICS.get(uri);
+ if (status == null) {
+ SERVICE_STATISTICS.putIfAbsent(uri, new RpcStatus());
+ status = SERVICE_STATISTICS.get(uri);
+ }
+ return status;
+ }
+
+ /**
+ *
+ * @param url
+ */
+ public static void removeStatus(URL url) {
+ String uri = url.toIdentityString();
+ SERVICE_STATISTICS.remove(uri);
+ }
+
+ /**
+ *
+ * @param url
+ * @param methodName
+ * @return status
+ */
+ public static RpcStatus getStatus(URL url, String methodName) {
+ String uri = url.toIdentityString();
+ ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);
+ if (map == null) {
+ METHOD_STATISTICS.putIfAbsent(uri, new ConcurrentHashMap<String, RpcStatus>());
+ map = METHOD_STATISTICS.get(uri);
+ }
+ RpcStatus status = map.get(methodName);
+ if (status == null) {
+ map.putIfAbsent(methodName, new RpcStatus());
+ status = map.get(methodName);
+ }
+ return status;
+ }
+
+ /**
+ *
+ * @param url
+ */
+ public static void removeStatus(URL url, String methodName) {
+ String uri = url.toIdentityString();
+ ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);
+ if (map != null) {
+ map.remove(methodName);
+ }
+ }
+
+ /**
+ *
+ * @param url
+ */
+ public static void beginCount(URL url, String methodName) {
+ beginCount(getStatus(url));
+ beginCount(getStatus(url, methodName));
+ }
+
+ private static void beginCount(RpcStatus status) {
+ status.active.incrementAndGet();
+ }
+
+ /**
+ *
+ * @param url
+ * @param elapsed
+ * @param succeeded
+ */
+ public static void endCount(URL url, String methodName, long elapsed, boolean succeeded) {
+ endCount(getStatus(url), elapsed, succeeded);
+ endCount(getStatus(url, methodName), elapsed, succeeded);
+ }
+
+ private static void endCount(RpcStatus status, long elapsed, boolean succeeded) {
+ status.active.decrementAndGet();
+ status.total.incrementAndGet();
+ status.totalElapsed.addAndGet(elapsed);
+ if (status.maxElapsed.get() < elapsed) {
+ status.maxElapsed.set(elapsed);
+ }
+ if (succeeded) {
+ if (status.succeededMaxElapsed.get() < elapsed) {
+ status.succeededMaxElapsed.set(elapsed);
+ }
+ } else {
+ status.failed.incrementAndGet();
+ status.failedElapsed.addAndGet(elapsed);
+ if (status.failedMaxElapsed.get() < elapsed) {
+ status.failedMaxElapsed.set(elapsed);
+ }
+ }
+ }
+
+ private final ConcurrentMap<String, Object> values = new ConcurrentHashMap<String, Object>();
+
+ private final AtomicInteger active = new AtomicInteger();
+
+ private final AtomicLong total = new AtomicLong();
+
+ private final AtomicInteger failed = new AtomicInteger();
+
+ private final AtomicLong totalElapsed = new AtomicLong();
+
+ private final AtomicLong failedElapsed = new AtomicLong();
+
+ private final AtomicLong maxElapsed = new AtomicLong();
+
+ private final AtomicLong failedMaxElapsed = new AtomicLong();
+
+ private final AtomicLong succeededMaxElapsed = new AtomicLong();
+
+ private RpcStatus() {}
+
+ /**
+ * set value.
+ *
+ * @param key
+ * @param value
+ */
+ public void set(String key, Object value) {
+ values.put(key, value);
+ }
+
+ /**
+ * get value.
+ *
+ * @param key
+ * @return value
+ */
+ public Object get(String key) {
+ return values.get(key);
+ }
+
+ /**
+ * get active.
+ *
+ * @return active
+ */
+ public int getActive() {
+ return active.get();
+ }
+
+ /**
+ * get total.
+ *
+ * @return total
+ */
+ public long getTotal() {
+ return total.longValue();
+ }
+
+ /**
+ * get total elapsed.
+ *
+ * @return total elapsed
+ */
+ public long getTotalElapsed() {
+ return totalElapsed.get();
+ }
+
+ /**
+ * get average elapsed.
+ *
+ * @return average elapsed
+ */
+ public long getAverageElapsed() {
+ long total = getTotal();
+ if (total == 0) {
+ return 0;
+ }
+ return getTotalElapsed() / total;
+ }
+
+ /**
+ * get max elapsed.
+ *
+ * @return max elapsed
+ */
+ public long getMaxElapsed() {
+ return maxElapsed.get();
+ }
+
+ /**
+ * get failed.
+ *
+ * @return failed
+ */
+ public int getFailed() {
+ return failed.get();
+ }
+
+ /**
+ * get failed elapsed.
+ *
+ * @return failed elapsed
+ */
+ public long getFailedElapsed() {
+ return failedElapsed.get();
+ }
+
+ /**
+ * get failed average elapsed.
+ *
+ * @return failed average elapsed
+ */
+ public long getFailedAverageElapsed() {
+ long failed = getFailed();
+ if (failed == 0) {
+ return 0;
+ }
+ return getFailedElapsed() / failed;
+ }
+
+ /**
+ * get failed max elapsed.
+ *
+ * @return failed max elapsed
+ */
+ public long getFailedMaxElapsed() {
+ return failedMaxElapsed.get();
+ }
+
+ /**
+ * get succeeded.
+ *
+ * @return succeeded
+ */
+ public long getSucceeded() {
+ return getTotal() - getFailed();
+ }
+
+ /**
+ * get succeeded elapsed.
+ *
+ * @return succeeded elapsed
+ */
+ public long getSucceededElapsed() {
+ return getTotalElapsed() - getFailedElapsed();
+ }
+
+ /**
+ * get succeeded average elapsed.
+ *
+ * @return succeeded average elapsed
+ */
+ public long getSucceededAverageElapsed() {
+ long succeeded = getSucceeded();
+ if (succeeded == 0) {
+ return 0;
+ }
+ return getSucceededElapsed() / succeeded;
+ }
+
+ /**
+ * get succeeded max elapsed.
+ *
+ * @return succeeded max elapsed.
+ */
+ public long getSucceededMaxElapsed() {
+ return succeededMaxElapsed.get();
+ }
+
+ /**
+ * Calculate average TPS (Transaction per second).
+ *
+ * @return tps
+ */
+ public long getAverageTps() {
+ if (getTotalElapsed() >= 1000L) {
+ return getTotal() / (getTotalElapsed() / 1000L);
+ }
+ return getTotal();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/StaticContext.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/StaticContext.java
new file mode 100644
index 0000000..14825b7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/AccessLogFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/AccessLogFilter.java
new file mode 100644
index 0000000..49e4ce9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/AccessLogFilter.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * 记录Service的Access Log。
+ * <p>
+ * 使用的Logger key是<code><b>dubbo.accesslog</b></code>。
+ * 如果想要配置Access Log只出现在指定的Appender中,可以在Log4j中注意配置上additivity。配置示例:
+ * <code>
+ * <pre>
+ * <logger name="<b>dubbo.accesslog</b>" <font color="red">additivity="false"</font>>
+ * <level value="info" />
+ * <appender-ref ref="foo" />
+ * </logger>
+ * </pre></code>
+ *
+ * @author ding.lid
+ */
+@Activate(group = Constants.PROVIDER, value = Constants.ACCESS_LOG_KEY)
+public class AccessLogFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(AccessLogFilter.class);
+
+ private static final String ACCESS_LOG_KEY = "dubbo.accesslog";
+
+ private static final String FILE_DATE_FORMAT = "yyyyMMdd";
+
+ private static final String MESSAGE_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+ private static final int LOG_MAX_BUFFER = 5000;
+
+ private static final long LOG_OUTPUT_INTERVAL = 5000;
+
+ private final ConcurrentMap<String, Set<String>> logQueue = new ConcurrentHashMap<String, Set<String>>();
+
+ private final ScheduledExecutorService logScheduled = Executors.newScheduledThreadPool(2, new NamedThreadFactory("Dubbo-Access-Log", true));
+
+ private volatile ScheduledFuture<?> logFuture = null;
+
+ private class LogTask implements Runnable {
+ public void run() {
+ try {
+ if (logQueue != null && logQueue.size() > 0) {
+ for (Map.Entry<String, Set<String>> entry : logQueue.entrySet()) {
+ try {
+ String accesslog = entry.getKey();
+ Set<String> logSet = entry.getValue();
+ File file = new File(accesslog);
+ File dir = file.getParentFile();
+ if (null!=dir&&! dir.exists()) {
+ dir.mkdirs();
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Append log to " + accesslog);
+ }
+ if (file.exists()) {
+ String now = new SimpleDateFormat(FILE_DATE_FORMAT).format(new Date());
+ String last = new SimpleDateFormat(FILE_DATE_FORMAT).format(new Date(file.lastModified()));
+ if (! now.equals(last)) {
+ File archive = new File(file.getAbsolutePath() + "." + last);
+ file.renameTo(archive);
+ }
+ }
+ FileWriter writer = new FileWriter(file, true);
+ try {
+ for (String msg : logSet) {
+ writer.write(msg);
+ writer.write("\r\n");
+ }
+ writer.flush();
+ } finally {
+ writer.close();
+ }
+ logSet.clear();
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ private void init() {
+ if (logFuture == null) {
+ synchronized (logScheduled) {
+ if (logFuture == null) {
+ logFuture = logScheduled.scheduleWithFixedDelay(new LogTask(), LOG_OUTPUT_INTERVAL, LOG_OUTPUT_INTERVAL, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+ }
+
+ private void log(String accesslog, String logmessage) {
+ init();
+ Set<String> logSet = logQueue.get(accesslog);
+ if (logSet == null) {
+ logQueue.putIfAbsent(accesslog, new ConcurrentHashSet<String>());
+ logSet = logQueue.get(accesslog);
+ }
+ if (logSet.size() < LOG_MAX_BUFFER) {
+ logSet.add(logmessage);
+ }
+ }
+
+ public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
+ try {
+ String accesslog = invoker.getUrl().getParameter(Constants.ACCESS_LOG_KEY);
+ if (ConfigUtils.isNotEmpty(accesslog)) {
+ RpcContext context = RpcContext.getContext();
+ String serviceName = invoker.getInterface().getName();
+ String version = invoker.getUrl().getParameter(Constants.VERSION_KEY);
+ String group = invoker.getUrl().getParameter(Constants.GROUP_KEY);
+ StringBuilder sn = new StringBuilder();
+ sn.append("[").append(new SimpleDateFormat(MESSAGE_DATE_FORMAT).format(new Date())).append("] ").append(context.getRemoteHost()).append(":").append(context.getRemotePort())
+ .append(" -> ").append(context.getLocalHost()).append(":").append(context.getLocalPort())
+ .append(" - ");
+ if (null != group && group.length() > 0) {
+ sn.append(group).append("/");
+ }
+ sn.append(serviceName);
+ if (null != version && version.length() > 0) {
+ sn.append(":").append(version);
+ }
+ sn.append(" ");
+ sn.append(inv.getMethodName());
+ sn.append("(");
+ Class<?>[] types = inv.getParameterTypes();
+ if (types != null && types.length > 0) {
+ boolean first = true;
+ for (Class<?> type : types) {
+ if (first) {
+ first = false;
+ } else {
+ sn.append(",");
+ }
+ sn.append(type.getName());
+ }
+ }
+ sn.append(") ");
+ Object[] args = inv.getArguments();
+ if (args != null && args.length > 0) {
+ sn.append(JSON.json(args));
+ }
+ String msg = sn.toString();
+ if (ConfigUtils.isDefault(accesslog)) {
+ LoggerFactory.getLogger(ACCESS_LOG_KEY + "." + invoker.getInterface().getName()).info(msg);
+ } else {
+ log(accesslog, msg);
+ }
+ }
+ } catch (Throwable t) {
+ logger.warn("Exception in AcessLogFilter of service(" + invoker + " -> " + inv + ")", t);
+ }
+ return invoker.invoke(inv);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilter.java
new file mode 100644
index 0000000..c7d33a3
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilter.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.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcStatus;
+
+/**
+ * LimitInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.CONSUMER, value = Constants.ACTIVES_KEY)
+public class ActiveLimitFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ URL url = invoker.getUrl();
+ String methodName = invocation.getMethodName();
+ int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
+ RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
+ if (max > 0) {
+ long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0);
+ long start = System.currentTimeMillis();
+ long remain = timeout;
+ int active = count.getActive();
+ if (active >= max) {
+ synchronized (count) {
+ while ((active = count.getActive()) >= 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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.java
new file mode 100644
index 0000000..db902c3
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * ClassLoaderInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.PROVIDER)
+public class ClassLoaderFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ ClassLoader ocl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(invoker.getInterface().getClassLoader());
+ try {
+ return invoker.invoke(invocation);
+ } finally {
+ Thread.currentThread().setContextClassLoader(ocl);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.java
new file mode 100644
index 0000000..7a5b5e5
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.CompatibleTypeUtils;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * CompatibleFilter
+ *
+ * @author william.liangf
+ */
+public class CompatibleFilter implements Filter {
+
+ private static Logger logger = LoggerFactory.getLogger(CompatibleFilter.class);
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ Result result = invoker.invoke(invocation);
+ if (! invocation.getMethodName().startsWith("$") && ! result.hasException()) {
+ Object value = result.getValue();
+ if (value != null) {
+ try {
+ Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
+ Class<?> type = method.getReturnType();
+ Object newValue;
+ String serialization = invoker.getUrl().getParameter(Constants.SERIALIZATION_KEY);
+ if ("json".equals(serialization)
+ || "fastjson".equals(serialization)){
+ Type gtype = method.getGenericReturnType();
+ newValue = PojoUtils.realize(value, type, gtype);
+ } else if (! type.isInstance(value)) {
+ newValue = PojoUtils.isPojo(type)
+ ? PojoUtils.realize(value, type)
+ : CompatibleTypeUtils.compatibleTypeConvert(value, type);
+
+ } else {
+ newValue = value;
+ }
+ if (newValue != value) {
+ result = new RpcResult(newValue);
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.java
new file mode 100644
index 0000000..7107865
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.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.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+/**
+ * ConsumerContextInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.CONSUMER, order = -10000)
+public class ConsumerContextFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ RpcContext.getContext()
+ .setInvoker(invoker)
+ .setInvocation(invocation)
+ .setLocalAddress(NetUtils.getLocalHost(), 0)
+ .setRemoteAddress(invoker.getUrl().getHost(),
+ invoker.getUrl().getPort());
+ if (invocation instanceof RpcInvocation) {
+ ((RpcInvocation)invocation).setInvoker(invoker);
+ }
+ try {
+ return invoker.invoke(invocation);
+ } finally {
+ RpcContext.getContext().clearAttachments();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ContextFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ContextFilter.java
new file mode 100644
index 0000000..f373690
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ContextFilter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+/**
+ * ContextInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.PROVIDER, order = -10000)
+public class ContextFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ Map<String, String> attachments = invocation.getAttachments();
+ if (attachments != null) {
+ attachments = new HashMap<String, String>(attachments);
+ attachments.remove(Constants.PATH_KEY);
+ attachments.remove(Constants.GROUP_KEY);
+ attachments.remove(Constants.VERSION_KEY);
+ attachments.remove(Constants.DUBBO_VERSION_KEY);
+ attachments.remove(Constants.TOKEN_KEY);
+ attachments.remove(Constants.TIMEOUT_KEY);
+ }
+ RpcContext.getContext()
+ .setInvoker(invoker)
+ .setInvocation(invocation)
+ .setAttachments(attachments)
+ .setLocalAddress(invoker.getUrl().getHost(),
+ invoker.getUrl().getPort());
+ if (invocation instanceof RpcInvocation) {
+ ((RpcInvocation)invocation).setInvoker(invoker);
+ }
+ try {
+ return invoker.invoke(invocation);
+ } finally {
+ RpcContext.removeContext();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilter.java
new file mode 100644
index 0000000..3b11ada
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * DeprecatedInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.CONSUMER, value = Constants.DEPRECATED_KEY)
+public class DeprecatedFilter implements Filter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DeprecatedFilter.class);
+
+ private static final Set<String> logged = new ConcurrentHashSet<String>();
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ String key = invoker.getInterface().getName() + "." + invocation.getMethodName();
+ if (! logged.contains(key)) {
+ logged.add(key);
+ if (invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.DEPRECATED_KEY, false)) {
+ LOGGER.error("The service method " + invoker.getInterface().getName() + "." + getMethodSignature(invocation) + " is DEPRECATED! Declare from " + invoker.getUrl());
+ }
+ }
+ return invoker.invoke(invocation);
+ }
+
+ private String getMethodSignature(Invocation invocation) {
+ StringBuilder buf = new StringBuilder(invocation.getMethodName());
+ buf.append("(");
+ Class<?>[] types = invocation.getParameterTypes();
+ if (types != null && types.length > 0) {
+ boolean first = true;
+ for (Class<?> type : types) {
+ if (first) {
+ first = false;
+ } else {
+ buf.append(", ");
+ }
+ buf.append(type.getSimpleName());
+ }
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/EchoFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/EchoFilter.java
new file mode 100644
index 0000000..21be52d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/EchoFilter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * EchoInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.PROVIDER)
+public class EchoFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
+ if(inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != null && inv.getArguments().length == 1 )
+ return new RpcResult(inv.getArguments()[0]);
+ return invoker.invoke(inv);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.java
new file mode 100644
index 0000000..4401ab7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.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.filter;
+
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * ExceptionInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.PROVIDER)
+public class ExceptionFilter implements Filter {
+
+ private final Logger logger;
+
+ public ExceptionFilter() {
+ this(LoggerFactory.getLogger(ExceptionFilter.class));
+ }
+
+ public ExceptionFilter(Logger logger) {
+ this.logger = logger;
+ }
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ try {
+ Result result = invoker.invoke(invocation);
+ if (result.hasException() && GenericService.class != invoker.getInterface()) {
+ try {
+ Throwable exception = result.getException();
+ // 如果是checked异常,直接抛出
+ if (! (exception instanceof RuntimeException)) {
+ return result;
+ }
+ // 在方法签名上有声明,直接抛出
+ 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;
+ }
+ // 未在方法签名上定义的异常,在服务器端打印ERROR日志
+ logger.error("Got unchecked and undeclared exception. service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
+ // 异常类和接口类在同一jar包里,直接抛出
+ String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
+ String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
+ if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){
+ return result;
+ }
+ // 是JDK自带的异常,直接抛出
+ String className = exception.getClass().getName();
+ if (className.startsWith("java.") || className.startsWith("javax.")) {
+ return result;
+ }
+ // 是Dubbo本身的异常,直接抛出
+ if (exception instanceof RpcException) {
+ return result;
+ }
+ // 否则,包装成RuntimeException抛给客户端
+ 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 undeclared exception. service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
+ throw e;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ExecuteLimitFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ExecuteLimitFilter.java
new file mode 100644
index 0000000..64c13c2
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ExecuteLimitFilter.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.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcStatus;
+
+/**
+ * ThreadLimitInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY)
+public class ExecuteLimitFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ URL url = invoker.getUrl();
+ String methodName = invocation.getMethodName();
+ int max = url.getMethodParameter(methodName, Constants.EXECUTES_KEY, 0);
+ if (max > 0) {
+ RpcStatus count = RpcStatus.getStatus(url, invocation.getMethodName());
+ if (count.getActive() >= max) {
+ throw new RpcException("Failed to invoke method " + invocation.getMethodName() + " in provider " + url + ", cause: The service using threads greater than <dubbo:service executes=\"" + max + "\" /> limited.");
+ }
+ }
+ long begin = System.currentTimeMillis();
+ boolean isException = false;
+ RpcStatus.beginCount(url, methodName);
+ try {
+ Result result = invoker.invoke(invocation);
+ return result;
+ } catch (Throwable t) {
+ isException = true;
+ if(t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ }
+ else {
+ throw new RpcException("unexpected exception when ExecuteLimitFilter", t);
+ }
+ }
+ finally {
+ RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, isException);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/GenericFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/GenericFilter.java
new file mode 100644
index 0000000..51d660e
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/GenericFilter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.service.GenericException;
+
+/**
+ * GenericInvokerFilter.
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.PROVIDER)
+public class GenericFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
+ if (inv.getMethodName().equals(Constants.$INVOKE)
+ && inv.getArguments() != null
+ && inv.getArguments().length == 3
+ && ! invoker.getUrl().getParameter(Constants.GENERIC_KEY, false)) {
+ String name = ((String) inv.getArguments()[0]).trim();
+ String[] types = (String[]) inv.getArguments()[1];
+ Object[] args = (Object[]) inv.getArguments()[2];
+ try {
+ Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
+ Class<?>[] params = method.getParameterTypes();
+ if (args == null) {
+ args = new Object[params.length];
+ }
+ args = PojoUtils.realize(args, params, method.getGenericParameterTypes());
+ Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
+ if (result.hasException()
+ && ! (result.getException() instanceof GenericException)) {
+ return new RpcResult(new GenericException(result.getException()));
+ }
+ return new RpcResult(PojoUtils.generalize(result.getValue()));
+ } catch (NoSuchMethodException e) {
+ throw new RpcException(e.getMessage(), e);
+ } catch (ClassNotFoundException e) {
+ throw new RpcException(e.getMessage(), e);
+ }
+ }
+ return invoker.invoke(inv);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.java
new file mode 100644
index 0000000..19ab00e
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.service.GenericException;
+
+/**
+ * GenericImplInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.CONSUMER, value = Constants.GENERIC_KEY)
+public class GenericImplFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(GenericImplFilter.class);
+
+ private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[] {String.class, String[].class, Object[].class};
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ if (invoker.getUrl().getParameter(Constants.GENERIC_KEY, false)
+ && ! Constants.$INVOKE.equals(invocation.getMethodName())
+ && invocation instanceof RpcInvocation) {
+ RpcInvocation invocation2 = (RpcInvocation) invocation;
+ String methodName = invocation2.getMethodName();
+ Class<?>[] parameterTypes = invocation2.getParameterTypes();
+ Object[] arguments = invocation2.getArguments();
+
+ String[] types = new String[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i ++) {
+ types[i] = ReflectUtils.getName(parameterTypes[i]);
+ }
+ Object[] args = PojoUtils.generalize(arguments);
+
+ invocation2.setMethodName(Constants.$INVOKE);
+ invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES);
+ invocation2.setArguments(new Object[] {methodName, types, args});
+ Result result = invoker.invoke(invocation2);
+
+ if (! result.hasException()) {
+ Object value = result.getValue();
+ try {
+ Method method = invoker.getInterface().getMethod(methodName, parameterTypes);
+ return new RpcResult(PojoUtils.realize(value, method.getReturnType(), method.getGenericReturnType()));
+ } catch (NoSuchMethodException e) {
+ throw new RpcException(e.getMessage(), e);
+ }
+ } else if (result.getException() instanceof GenericException) {
+ GenericException exception = (GenericException) result.getException();
+ try {
+ String className = exception.getExceptionClass();
+ Class<?> clazz = ReflectUtils.forName(className);
+ Throwable targetException = null;
+ Throwable lastException = null;
+ try {
+ targetException = (Throwable) clazz.newInstance();
+ } catch (Throwable e) {
+ lastException = e;
+ for (Constructor<?> constructor : clazz.getConstructors()) {
+ try {
+ targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]);
+ break;
+ } catch (Throwable e1) {
+ lastException = e1;
+ }
+ }
+ }
+ if (targetException != null) {
+ try {
+ Field field = Throwable.class.getDeclaredField("detailMessage");
+ if (! field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ field.set(targetException, exception.getExceptionMessage());
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ result = new RpcResult(targetException);
+ } else if (lastException != null) {
+ throw lastException;
+ }
+ } catch (Throwable e) {
+ throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e);
+ }
+ }
+ return result;
+ }
+ return invoker.invoke(invocation);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.java
new file mode 100644
index 0000000..b63ff23
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.util.Arrays;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * 如果执行timeout,则log记录下,不干涉服务的运行
+ *
+ * @author chao.liuc
+ */
+@Activate(group = Constants.PROVIDER)
+public class TimeoutFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(TimeoutFilter.class);
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ long start = System.currentTimeMillis();
+ Result result = invoker.invoke(invocation);
+ long elapsed = System.currentTimeMillis() - start;
+ if (invoker.getUrl() != null
+ && elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(),
+ "timeout", Integer.MAX_VALUE)) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("invoke time out. method: " + invocation.getMethodName()
+ + "arguments: " + Arrays.toString(invocation.getArguments()) + " , url is "
+ + invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/TokenFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/TokenFilter.java
new file mode 100644
index 0000000..5ff698e
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/TokenFilter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * TokenInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.PROVIDER, value = Constants.TOKEN_KEY)
+public class TokenFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation inv)
+ throws RpcException {
+ String token = invoker.getUrl().getParameter(Constants.TOKEN_KEY);
+ if (ConfigUtils.isNotEmpty(token)) {
+ Class<?> serviceType = invoker.getInterface();
+ Map<String, String> attachments = inv.getAttachments();
+ String remoteToken = attachments == null ? null : attachments.get(Constants.TOKEN_KEY);
+ if (! token.equals(remoteToken)) {
+ throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() + "() from consumer " + RpcContext.getContext().getRemoteHost() + " to provider " + RpcContext.getContext().getLocalHost());
+ }
+ }
+ return invoker.invoke(inv);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilter.java
new file mode 100644
index 0000000..886178e
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcStatus;
+
+/**
+ * 限制 service 或方法的 tps.
+ *
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+@Activate(group = Constants.PROVIDER, value = Constants.TPS_MAX_KEY)
+public class TpsLimitFilter implements Filter {
+
+// TODO 现在依赖 ActiveLimitFilter 或 ExecuteLimitFilter 的计数,需要放到这两个 filter 后执行
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+
+ long max = invoker.getUrl().getMethodParameter(
+ invocation.getMethodName(), Constants.TPS_MAX_KEY, 0L);
+ // verify method tps
+ if (max > 0L
+ && max <= RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getAverageTps()) {
+ throw new RpcException(
+ new StringBuilder(64)
+ .append("Failed to invoke service ")
+ .append(invoker.getInterface().getName())
+ .append(".")
+ .append(invocation.getMethodName())
+ .append(" because exceed max service tps ")
+ .append(max).toString());
+ }
+
+ return invoker.invoke(invocation);
+ }
+
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java
new file mode 100644
index 0000000..aecd7f2
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.listener;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * DeprecatedProtocolFilter
+ *
+ * @author william.liangf
+ */
+@Activate(Constants.DEPRECATED_KEY)
+public class DeprecatedInvokerListener extends InvokerListenerAdapter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DeprecatedInvokerListener.class);
+
+ public void referred(Invoker<?> invoker) throws RpcException {
+ if (invoker.getUrl().getParameter(Constants.DEPRECATED_KEY, false)) {
+ LOGGER.error("The service " + invoker.getInterface().getName() + " is DEPRECATED! Declare from " + invoker.getUrl());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/ExporterListenerAdapter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/ExporterListenerAdapter.java
new file mode 100644
index 0000000..1859ac1
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/InvokerListenerAdapter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/InvokerListenerAdapter.java
new file mode 100644
index 0000000..57cfc3c
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerExporterWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerExporterWrapper.java
new file mode 100644
index 0000000..af37329
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerInvokerWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerInvokerWrapper.java
new file mode 100644
index 0000000..767c733
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractExporter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractExporter.java
new file mode 100644
index 0000000..d529354
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractInvoker.java
new file mode 100644
index 0000000..87732ff
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractInvoker.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.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;
+import com.alibaba.dubbo.rpc.support.RpcUtils;
+
+/**
+ * AbstractInvoker.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public abstract class AbstractInvoker<T> implements Invoker<T> {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final Class<T> type;
+
+ private final URL url;
+
+ private final Map<String, String> attachment;
+
+ private volatile boolean available = true;
+
+ private volatile boolean destroyed = false;
+
+ public AbstractInvoker(Class<T> type, URL url){
+ this(type, url, (Map<String, String>) null);
+ }
+
+ public AbstractInvoker(Class<T> type, URL url, String[] keys) {
+ this(type, url, convertAttachment(url, keys));
+ }
+
+ public AbstractInvoker(Class<T> type, URL url, Map<String, String> attachment) {
+ if (type == null)
+ throw new IllegalArgumentException("service type == null");
+ if (url == null)
+ throw new IllegalArgumentException("service url == null");
+ this.type = type;
+ this.url = url;
+ this.attachment = attachment == null ? null : Collections.unmodifiableMap(attachment);
+ }
+
+ private static Map<String, String> convertAttachment(URL url, String[] keys) {
+ if (keys == null || keys.length == 0) {
+ return null;
+ }
+ Map<String, String> attachment = new HashMap<String, String>();
+ for (String key : keys) {
+ String value = url.getParameter(key);
+ if (value != null && value.length() > 0) {
+ attachment.put(key, value);
+ }
+ }
+ return attachment;
+ }
+
+ public Class<T> getInterface() {
+ return type;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isAvailable() {
+ return available;
+ }
+
+ protected void setAvailable(boolean available) {
+ this.available = available;
+ }
+
+ public void destroy() {
+ if (isDestroyed()) {
+ return;
+ }
+ destroyed = true;
+ setAvailable(false);
+ }
+
+ public boolean isDestroyed() {
+ return destroyed;
+ }
+
+ public String toString() {
+ return getInterface() + " -> " + getUrl()==null?" ":getUrl().toString();
+ }
+
+ public Result invoke(Invocation inv) throws RpcException {
+ if(destroyed) {
+ throw new RpcException("Rpc invoker for service " + this + " on consumer " + NetUtils.getLocalHost()
+ + " use dubbo version " + Version.getVersion()
+ + " is DESTROYED, can not be invoked any more!");
+ }
+ RpcInvocation invocation = (RpcInvocation) inv;
+ invocation.setInvoker(this);
+ Map<String, String> attachments = new HashMap<String, String>();
+ if (attachment != null && attachment.size() > 0) {
+ attachments.putAll(attachment);
+ }
+ Map<String, String> context = RpcContext.getContext().getAttachments();
+ if (context != null) {
+ attachments.putAll(context);
+ }
+ if (invocation.getAttachments() != null) {
+ attachments.putAll(invocation.getAttachments());
+ }
+ if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
+ attachments.put(Constants.Attachments.IS_ASYNC_KEY, Boolean.TRUE.toString());
+ }
+ RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
+
+ 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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.java
new file mode 100644
index 0000000..af05c50
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+
+/**
+ * abstract ProtocolSupport.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public abstract class AbstractProtocol implements Protocol {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
+
+ //TODO SOFEREFENCE
+ protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();
+
+ protected static String serviceKey(URL url) {
+ return serviceKey(url.getPort(), url.getPath(), url.getParameter(Constants.VERSION_KEY),
+ url.getParameter(Constants.GROUP_KEY));
+ }
+
+ protected static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) {
+ StringBuilder buf = new StringBuilder();
+ if (serviceGroup != null && serviceGroup.length() > 0) {
+ buf.append(serviceGroup);
+ buf.append("/");
+ }
+ buf.append(serviceName);
+ if (serviceVersion != null && serviceVersion.length() > 0 && ! "0.0.0".equals(serviceVersion)) {
+ buf.append(":");
+ buf.append(serviceVersion);
+ }
+ buf.append(":");
+ buf.append(port);
+ return buf.toString();
+ }
+
+ public void destroy() {
+ for (Invoker<?> invoker : invokers){
+ if (invoker != null) {
+ invokers.remove(invoker);
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Destroy reference: " + invoker.getUrl());
+ }
+ invoker.destroy();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ for (String key : new ArrayList<String>(exporterMap.keySet())) {
+ Exporter<?> exporter = exporterMap.remove(key);
+ if (exporter != null) {
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Unexport service: " + exporter.getInvoker().getUrl());
+ }
+ exporter.unexport();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ }
+ @SuppressWarnings("deprecation")
+ protected static int getServerShutdownTimeout() {
+ int timeout = Constants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT;
+ String value = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_KEY);
+ if (value != null && value.length() > 0) {
+ try{
+ timeout = Integer.parseInt(value);
+ }catch (Exception e) {
+ }
+ } else {
+ value = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_SECONDS_KEY);
+ if (value != null && value.length() > 0) {
+ try{
+ timeout = Integer.parseInt(value) * 1000;
+ }catch (Exception e) {
+ }
+ }
+ }
+
+ return timeout;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProxyProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProxyProtocol.java
new file mode 100644
index 0000000..d5277c9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProxyProtocol.java
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+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.ProxyFactory;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * AbstractProxyProtocol
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractProxyProtocol extends AbstractProtocol {
+
+ private final List<Class<?>> rpcExceptions = new CopyOnWriteArrayList<Class<?>>();;
+
+ private ProxyFactory proxyFactory;
+
+ public AbstractProxyProtocol() {
+ }
+
+ public AbstractProxyProtocol(Class<?>... exceptions) {
+ for (Class<?> exception : exceptions) {
+ addRpcException(exception);
+ }
+ }
+
+ public void addRpcException(Class<?> exception) {
+ this.rpcExceptions.add(exception);
+ }
+
+ public void setProxyFactory(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ public ProxyFactory getProxyFactory() {
+ return proxyFactory;
+ }
+
+ public <T> Exporter<T> export(final Invoker<T> invoker) throws RpcException {
+ final String uri = invoker.getUrl().getAbsolutePath();
+ final Runnable runnable = doExport(proxyFactory.getProxy(invoker), invoker.getInterface(), invoker.getUrl());
+ Exporter<T> exporter = new AbstractExporter<T>(invoker) {
+ public void unexport() {
+ super.unexport();
+ exporterMap.remove(uri);
+ if (runnable != null) {
+ try {
+ runnable.run();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ };
+ exporterMap.put(uri, exporter);
+ return exporter;
+ }
+
+ public <T> Invoker<T> refer(final Class<T> type, final URL url) throws RpcException {
+ final Invoker<T> tagert = proxyFactory.getInvoker(doRefer(type, url), type, url);
+ Invoker<T> invoker = new AbstractInvoker<T>(type, url) {
+ @Override
+ protected Result doInvoke(Invocation invocation) throws Throwable {
+ try {
+ Result result = tagert.invoke(invocation);
+ Throwable e = result.getException();
+ if (e != null) {
+ for (Class<?> rpcException : rpcExceptions) {
+ if (rpcException.isAssignableFrom(e.getClass())) {
+ throw getRpcException(type, url, invocation, e);
+ }
+ }
+ }
+ return result;
+ } catch (RpcException e) {
+ if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) {
+ e.setCode(getErrorCode(e.getCause()));
+ }
+ throw e;
+ } catch (Throwable e) {
+ throw getRpcException(type, url, invocation, e);
+ }
+ }
+ };
+ invokers.add(invoker);
+ return invoker;
+ }
+
+ protected RpcException getRpcException(Class<?> type, URL url, Invocation invocation, Throwable e) {
+ RpcException re = new RpcException("Failed to invoke remote service: " + type + ", method: "
+ + invocation.getMethodName() + ", cause: " + e.getMessage(), e);
+ re.setCode(getErrorCode(e));
+ return re;
+ }
+
+ protected int getErrorCode(Throwable e) {
+ return RpcException.UNKNOWN_EXCEPTION;
+ }
+
+ protected abstract <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException;
+
+ protected abstract <T> T doRefer(Class<T> type, URL url) throws RpcException;
+
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/InvokerWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/InvokerWrapper.java
new file mode 100644
index 0000000..4f54dc6
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.java
new file mode 100644
index 0000000..ad760f5
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * ListenerProtocol
+ *
+ * @author william.liangf
+ */
+public class ProtocolFilterWrapper implements Protocol {
+
+ private final Protocol protocol;
+
+ public ProtocolFilterWrapper(Protocol protocol){
+ if (protocol == null) {
+ throw new IllegalArgumentException("protocol == null");
+ }
+ this.protocol = protocol;
+ }
+
+ public int getDefaultPort() {
+ return protocol.getDefaultPort();
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
+ return protocol.export(invoker);
+ }
+ return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
+ }
+
+ public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+ if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
+ return protocol.refer(type, url);
+ }
+ return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
+ }
+
+ public void destroy() {
+ protocol.destroy();
+ }
+
+ private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
+ Invoker<T> last = invoker;
+ List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
+ if (filters.size() > 0) {
+ for (int i = filters.size() - 1; i >= 0; i --) {
+ final Filter filter = filters.get(i);
+ final Invoker<T> next = last;
+ last = new Invoker<T>() {
+
+ public Class<T> getInterface() {
+ return invoker.getInterface();
+ }
+
+ public URL getUrl() {
+ return invoker.getUrl();
+ }
+
+ public boolean isAvailable() {
+ return invoker.isAvailable();
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ return filter.invoke(next, invocation);
+ }
+
+ public void destroy() {
+ invoker.destroy();
+ }
+
+ @Override
+ public String toString() {
+ return invoker.toString();
+ }
+ };
+ }
+ }
+ return last;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.java
new file mode 100644
index 0000000..f10348b
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol;
+
+import java.util.Collections;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.ExporterListener;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.InvokerListener;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.listener.ListenerExporterWrapper;
+import com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper;
+
+/**
+ * ListenerProtocol
+ *
+ * @author william.liangf
+ */
+public class ProtocolListenerWrapper implements Protocol {
+
+ private final Protocol protocol;
+
+ public ProtocolListenerWrapper(Protocol protocol){
+ if (protocol == null) {
+ throw new IllegalArgumentException("protocol == null");
+ }
+ this.protocol = protocol;
+ }
+
+ public int getDefaultPort() {
+ return protocol.getDefaultPort();
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
+ return protocol.export(invoker);
+ }
+ return new ListenerExporterWrapper<T>(protocol.export(invoker),
+ Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
+ .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
+ }
+
+ public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+ if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
+ return protocol.refer(type, url);
+ }
+ return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
+ Collections.unmodifiableList(
+ ExtensionLoader.getExtensionLoader(InvokerListener.class)
+ .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
+ }
+
+ public void destroy() {
+ protocol.destroy();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyFactory.java
new file mode 100644
index 0000000..3ee576b
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyInvoker.java
new file mode 100644
index 0000000..d0f2530
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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 method " + invocation.getMethodName() + " 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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/InvokerInvocationHandler.java
new file mode 100644
index 0000000..9e21bec
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
new file mode 100644
index 0000000..7e776af
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy.javassist;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.bytecode.Proxy;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory;
+import com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker;
+import com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler;
+
+/**
+ * JavaassistRpcProxyFactory
+
+ * @author william.liangf
+ */
+public class JavassistProxyFactory extends AbstractProxyFactory {
+
+ @SuppressWarnings("unchecked")
+ public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
+ return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
+ }
+
+ public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
+ // TODO Wrapper类不能正确处理带$的类名
+ final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
+ return new AbstractProxyInvoker<T>(proxy, type, url) {
+ @Override
+ protected Object doInvoke(T proxy, String methodName,
+ Class<?>[] parameterTypes,
+ Object[] arguments) throws Throwable {
+ return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java
new file mode 100644
index 0000000..afa8054
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy.jdk;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory;
+import com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker;
+import com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler;
+
+/**
+ * JavaassistRpcProxyFactory
+
+ * @author william.liangf
+ */
+public class JdkProxyFactory extends AbstractProxyFactory {
+
+ @SuppressWarnings("unchecked")
+ public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
+ return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
+ }
+
+ public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
+ return new AbstractProxyInvoker<T>(proxy, type, url) {
+ @Override
+ protected Object doInvoke(T proxy, String methodName,
+ Class<?>[] parameterTypes,
+ Object[] arguments) throws Throwable {
+ Method method = proxy.getClass().getMethod(methodName, parameterTypes);
+ return method.invoke(proxy, arguments);
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java
new file mode 100644
index 0000000..23b4378
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy.wrapper;
+
+import java.lang.reflect.Constructor;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * StubProxyFactoryWrapper
+ *
+ * @author william.liangf
+ */
+public class StubProxyFactoryWrapper implements ProxyFactory {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(StubProxyFactoryWrapper.class);
+
+ private final ProxyFactory proxyFactory;
+
+ private Protocol protocol;
+
+ public StubProxyFactoryWrapper(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public <T> T getProxy(Invoker<T> invoker) throws RpcException {
+ T proxy = proxyFactory.getProxy(invoker);
+ if (GenericService.class != invoker.getInterface()) {
+ String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY));
+ if (ConfigUtils.isNotEmpty(stub)) {
+ Class<?> serviceType = invoker.getInterface();
+ if (ConfigUtils.isDefault(stub)) {
+ if (invoker.getUrl().hasParameter(Constants.STUB_KEY)) {
+ stub = serviceType.getName() + "Stub";
+ } else {
+ stub = serviceType.getName() + "Local";
+ }
+ }
+ try {
+ Class<?> stubClass = ReflectUtils.forName(stub);
+ if (! serviceType.isAssignableFrom(stubClass)) {
+ throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + serviceType.getName());
+ }
+ try {
+ Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);
+ proxy = (T) constructor.newInstance(new Object[] {proxy});
+ //export stub service
+ URL url = invoker.getUrl();
+ if (url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT)){
+ url = url.addParameter(Constants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
+ url = url.addParameter(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
+ try{
+ export(proxy, (Class)invoker.getInterface(), url);
+ }catch (Exception e) {
+ LOGGER.error("export a stub service error.", e);
+ }
+ }
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implemention class " + stubClass.getName(), e);
+ }
+ } catch (Throwable t) {
+ LOGGER.error("Failed to create stub implemention class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
+ // ignore
+ }
+ }
+ }
+ return proxy;
+ }
+
+ public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
+ return proxyFactory.getInvoker(proxy, type, url);
+ }
+
+ private <T> Exporter<T> export(T instance, Class<T> type, URL url) {
+ return protocol.export(proxyFactory.getInvoker(instance, type, url));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/service/EchoService.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/service/EchoService.java
new file mode 100644
index 0000000..c09fcd9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/service/GenericException.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/service/GenericException.java
new file mode 100644
index 0000000..b6c2ebe
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/service/GenericService.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/service/GenericService.java
new file mode 100644
index 0000000..bd621b7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/DelegateExporter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/DelegateExporter.java
new file mode 100644
index 0000000..5b0eeec
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/DelegateExporter.java
@@ -0,0 +1,45 @@
+/**
+ * Project: dubbo-rpc
+ *
+ * File Created at 2012-2-24
+ * $Id$
+ *
+ * Copyright 1999-2100 Alibaba.com Corporation Limited.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Alibaba Company. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Alibaba.com.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * DelegateExporter
+ * @author chao.liuc
+ *
+ */
+public class DelegateExporter<T> implements Exporter<T> {
+
+ private final Exporter<T> exporter;
+
+ public DelegateExporter(Exporter<T> exporter) {
+ if (exporter == null) {
+ throw new IllegalArgumentException("exporter can not be null");
+ } else {
+ this.exporter = exporter;
+ }
+
+ }
+
+ public Invoker<T> getInvoker() {
+ return exporter.getInvoker();
+ }
+ public void unexport() {
+ exporter.unexport();
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/DelegateInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/DelegateInvoker.java
new file mode 100644
index 0000000..016f785
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
new file mode 100644
index 0000000..4d95683
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * @author chao.liuc
+ * @author william.liangf
+ *
+ */
+final public class MockInvoker<T> implements Invoker<T> {
+ private final static ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ private final static Map<String, Invoker<?>> mocks = new ConcurrentHashMap<String, Invoker<?>>();
+ private final static Map<String, Throwable> throwables = new ConcurrentHashMap<String, Throwable>();
+
+ private final URL url ;
+
+ public MockInvoker(URL url) {
+ this.url = url;
+ }
+ public Result invoke(Invocation invocation) throws RpcException {
+ String mock = getUrl().getParameter(invocation.getMethodName()+"."+Constants.MOCK_KEY);
+ if (invocation instanceof RpcInvocation) {
+ ((RpcInvocation) invocation).setInvoker(this);
+ }
+ if (StringUtils.isBlank(mock)){
+ mock = getUrl().getParameter(Constants.MOCK_KEY);
+ }
+
+ if (StringUtils.isBlank(mock)){
+ throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));
+ }
+ mock = normallizeMock(URL.decode(mock));
+ if (Constants.RETURN_PREFIX.trim().equalsIgnoreCase(mock.trim())){
+ RpcResult result = new RpcResult();
+ result.setValue(null);
+ return result;
+ } else if (mock.startsWith(Constants.RETURN_PREFIX)) {
+ mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();
+ mock = mock.replace('`', '"');
+ try {
+ Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
+ Object value = parseMockValue(mock, returnTypes);
+ return new RpcResult(value);
+ } catch (Exception ew) {
+ throw new RpcException("mock return invoke error. method :" + invocation.getMethodName() + ", mock:" + mock + ", url: "+ url , ew);
+ }
+ } else if (mock.startsWith(Constants.THROW_PREFIX)) {
+ mock = mock.substring(Constants.THROW_PREFIX.length()).trim();
+ mock = mock.replace('`', '"');
+ if (StringUtils.isBlank(mock)){
+ throw new RpcException(" mocked exception for Service degradation. ");
+ } else { //用户自定义类
+ Throwable t = getThrowable(mock);
+ throw new RpcException(RpcException.BIZ_EXCEPTION, t);
+ }
+ } else { //impl mock
+ try {
+ Invoker<T> invoker = getInvoker(mock);
+ return invoker.invoke(invocation);
+ } catch (Throwable t) {
+ throw new RpcException("Failed to create mock implemention class " + mock , t);
+ }
+ }
+ }
+
+ private Throwable getThrowable(String throwstr){
+ Throwable throwable =(Throwable) throwables.get(throwstr);
+ if (throwable != null ){
+ return throwable;
+ } else {
+ Throwable t = null;
+ try {
+ Class<?> bizException = ReflectUtils.forName(throwstr);
+ Constructor<?> constructor;
+ constructor = ReflectUtils.findConstructor(bizException, String.class);
+ t = (Throwable) constructor.newInstance(new Object[] {" mocked exception for Service degradation. "});
+ if (throwables.size() < 1000) {
+ throwables.put(throwstr, t);
+ }
+ } catch (Exception e) {
+ throw new RpcException("mock throw error :" + throwstr + " argument error.", e);
+ }
+ return t;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Invoker<T> getInvoker(String mockService){
+ Invoker<T> invoker =(Invoker<T>) mocks.get(mockService);
+ if (invoker != null ){
+ return invoker;
+ } else {
+ Class<T> serviceType = (Class<T>)ReflectUtils.forName(url.getServiceInterface());
+ if (ConfigUtils.isDefault(mockService)) {
+ mockService = serviceType.getName() + "Mock";
+ }
+
+ Class<?> mockClass = ReflectUtils.forName(mockService);
+ if (! serviceType.isAssignableFrom(mockClass)) {
+ throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
+ }
+
+ if (! serviceType.isAssignableFrom(mockClass)) {
+ throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
+ }
+ try {
+ T mockObject = (T) mockClass.newInstance();
+ invoker = proxyFactory.getInvoker(mockObject, (Class<T>)serviceType, url);
+ if (mocks.size() < 10000) {
+ mocks.put(mockService, invoker);
+ }
+ return invoker;
+ } catch (InstantiationException e) {
+ throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+ //mock=fail:throw
+ //mock=fail:return
+ //mock=xx.Service
+ private String normallizeMock(String mock) {
+ if (mock == null || mock.trim().length() ==0){
+ return mock;
+ } else if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock.trim()) || "force".equalsIgnoreCase(mock.trim())){
+ mock = url.getServiceInterface()+"Mock";
+ }
+ if (mock.startsWith(Constants.FAIL_PREFIX)) {
+ mock = mock.substring(Constants.FAIL_PREFIX.length()).trim();
+ } else if (mock.startsWith(Constants.FORCE_PREFIX)) {
+ mock = mock.substring(Constants.FORCE_PREFIX.length()).trim();
+ }
+ return mock;
+ }
+
+ public static Object parseMockValue(String mock) throws Exception {
+ return parseMockValue(mock, null);
+ }
+
+ public static Object parseMockValue(String mock, Type[] returnTypes) throws Exception {
+ Object value = null;
+ if ("empty".equals(mock)) {
+ value = ReflectUtils.getEmptyObject(returnTypes != null && returnTypes.length > 0 ? (Class<?>)returnTypes[0] : null);
+ } else if ("null".equals(mock)) {
+ value = null;
+ } else if ("true".equals(mock)) {
+ value = true;
+ } else if ("false".equals(mock)) {
+ value = false;
+ } else if (mock.length() >=2 && (mock.startsWith("\"") && mock.endsWith("\"")
+ || mock.startsWith("\'") && mock.endsWith("\'"))) {
+ value = mock.subSequence(1, mock.length() - 1);
+ } else if (returnTypes !=null && returnTypes.length >0 && returnTypes[0] == String.class) {
+ value = mock;
+ } else if (StringUtils.isNumeric(mock)) {
+ value = JSON.parse(mock);
+ }else if (mock.startsWith("{")) {
+ value = JSON.parse(mock, Map.class);
+ } else if (mock.startsWith("[")) {
+ value = JSON.parse(mock, List.class);
+ } else {
+ value = mock;
+ }
+ if (returnTypes != null && returnTypes.length > 0) {
+ value = PojoUtils.realize(value, (Class<?>)returnTypes[0], returnTypes.length > 1 ? returnTypes[1] : null);
+ }
+ return value;
+ }
+
+ public URL getUrl() {
+ return this.url;
+ }
+
+ public boolean isAvailable() {
+ return true;
+ }
+
+ public void destroy() {
+ //do nothing
+ }
+
+ public Class<T> getInterface() {
+ //FIXME
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockProtocol.java
new file mode 100644
index 0000000..fe23c24
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockProtocol.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;
+
+/**
+ * MockProtocol 用于在consumer side 通过url及类型生成一个mockInvoker
+ * @author chao.liuc
+ *
+ */
+final public class MockProtocol extends AbstractProtocol {
+
+ public int getDefaultPort() {
+ return 0;
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ throw new UnsupportedOperationException();
+ }
+
+ public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+ return new MockInvoker<T>(url);
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java
new file mode 100644
index 0000000..92eb839
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java
@@ -0,0 +1,150 @@
+/*
+ * 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 java.util.concurrent.atomic.AtomicLong;
+
+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.ReflectUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+/**
+ * RpcUtils
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class RpcUtils {
+
+ private static final Logger logger = LoggerFactory.getLogger(RpcUtils.class);
+
+ public static Class<?> getReturnType(Invocation invocation) {
+ try {
+ if (invocation != null && invocation.getInvoker() != null
+ && invocation.getInvoker().getUrl() != null
+ && ! invocation.getMethodName().startsWith("$")) {
+ String service = invocation.getInvoker().getUrl().getServiceInterface();
+ if (service != null && service.length() > 0) {
+ Class<?> cls = ReflectUtils.forName(service);
+ Method method = cls.getMethod(invocation.getMethodName(), invocation.getParameterTypes());
+ if (method.getReturnType() == void.class) {
+ return null;
+ }
+ return method.getReturnType();
+ }
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ return null;
+ }
+
+ public static Type[] getReturnTypes(Invocation invocation) {
+ try {
+ if (invocation != null && invocation.getInvoker() != null
+ && invocation.getInvoker().getUrl() != null
+ && ! invocation.getMethodName().startsWith("$")) {
+ String service = invocation.getInvoker().getUrl().getServiceInterface();
+ if (service != null && service.length() > 0) {
+ Class<?> cls = ReflectUtils.forName(service);
+ Method method = cls.getMethod(invocation.getMethodName(), invocation.getParameterTypes());
+ if (method.getReturnType() == void.class) {
+ return null;
+ }
+ return new Type[]{method.getReturnType(), method.getGenericReturnType()};
+ }
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ return null;
+ }
+
+ private static final AtomicLong INVOKE_ID = new AtomicLong(0);
+
+ public static Long getInvocationId(Invocation inv) {
+ String id = inv.getAttachment(Constants.Attachments.INVOCATIONID_KEY);
+ return id == null ? null : new Long(id);
+ }
+
+ /**
+ * 幂等操作:异步操作默认添加invocation id
+ * @param url
+ * @param inv
+ */
+ public static void attachInvocationIdIfAsync(URL url, Invocation inv){
+ if (isAttachInvocationId(url, inv) && getInvocationId(inv) == null && inv instanceof RpcInvocation) {
+ ((RpcInvocation)inv).setAttachment(Constants.Attachments.INVOCATIONID_KEY, String.valueOf(INVOKE_ID.getAndIncrement()));
+ }
+ }
+
+ private static boolean isAttachInvocationId(URL url , Invocation invocation) {
+ String value = url.getMethodParameter(invocation.getMethodName(), Constants.AUTO_ATTACH_INVOCATIONID_KEY);
+ if ( value == null ) {
+ //异步操作默认添加invocationid
+ return isAsync(url,invocation) ;
+ } else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
+ //设置为添加,则一定添加
+ return true;
+ } else {
+ //value为false时,不添加
+ return false;
+ }
+ }
+
+ public static String getMethodName(Invocation invocation){
+ String methodName;
+ if(Constants.$INVOKE.equals(invocation.getMethodName())
+ && invocation.getArguments() != null
+ && invocation.getArguments().length >0
+ && invocation.getArguments()[0] != null){
+ //the frist argument must be real method name;
+ methodName = invocation.getArguments()[0].toString();
+ }else {
+ methodName = invocation.getMethodName();
+ }
+ return methodName;
+ }
+
+ public static boolean isAsync(URL url, Invocation inv) {
+ boolean isAsync ;
+ //如果Java代码中设置优先.
+ if (Boolean.TRUE.toString().equals(inv.getAttachment(Constants.Attachments.IS_ASYNC_KEY))) {
+ isAsync = true;
+ } else {
+ isAsync = url.getMethodParameter(getMethodName(inv), Constants.ASYNC_KEY, false);
+ }
+ return isAsync;
+ }
+
+ public static boolean isOneway(URL url, Invocation inv) {
+ boolean isOneway ;
+ //如果Java代码中设置优先.
+ if (Boolean.TRUE.toString().equals(inv.getAttachment(Constants.Attachments.IS_ONEWAY_KEY))) {
+ isOneway = true;
+ } else {
+ isOneway = url.getMethodParameter(getMethodName(inv), Constants.RETURN_KEY, false);
+ }
+ return isOneway;
+ }
+
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..2689465
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1,14 @@
+echo=com.alibaba.dubbo.rpc.filter.EchoFilter
+generic=com.alibaba.dubbo.rpc.filter.GenericFilter
+genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter
+token=com.alibaba.dubbo.rpc.filter.TokenFilter
+accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter
+activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
+classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
+context=com.alibaba.dubbo.rpc.filter.ContextFilter
+consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
+exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
+executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
+deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter
+compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter
+timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener
new file mode 100644
index 0000000..6ec09b5
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener
@@ -0,0 +1 @@
+deprecated=com.alibaba.dubbo.rpc.listener.DeprecatedInvokerListener
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..6d823d6
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1,3 @@
+filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
+listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
+mock=com.alibaba.dubbo.rpc.support.MockProtocol
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory
new file mode 100644
index 0000000..d281b8a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory
@@ -0,0 +1,3 @@
+stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
+jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory
+javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/CustomArgument.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/CustomArgument.java
new file mode 100644
index 0000000..fc8af58
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/DemoRequest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/DemoRequest.java
new file mode 100644
index 0000000..665180d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/ProtocolUtils.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/ProtocolUtils.java
new file mode 100644
index 0000000..73f5d11
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/ProtocolUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+
+/**
+ * TODO Comment of ProtocolUtils
+ * @author william.liangf
+ *
+ */
+public class ProtocolUtils {
+
+ private static Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private static ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public static <T> T refer(Class<T> type, String url) {
+ return refer(type, URL.valueOf(url));
+ }
+
+ public static <T> T refer(Class<T> type, URL url) {
+ return proxy.getProxy(protocol.refer(type, url));
+ }
+
+ public static <T> Exporter<T> export(T instance, Class<T> type, String url) {
+ return export(instance, type, URL.valueOf(url));
+ }
+
+ public static <T> Exporter<T> export(T instance, Class<T> type, URL url) {
+ return protocol.export(proxy.getInvoker(instance, type, url));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/AccessLogFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/AccessLogFilterTest.java
new file mode 100644
index 0000000..26cd544
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/AccessLogFilterTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.LogUtil;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MyInvoker;
+
+/**
+ * AccessLogFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class AccessLogFilterTest {
+
+ Filter accessLogFilter = new AccessLogFilter();
+
+ // 测试filter不会抛出异常
+ @Test
+ public void testInvokeException() {
+ Invoker<AccessLogFilterTest> invoker = new MyInvoker<AccessLogFilterTest>(null);
+ Invocation invocation = new MockInvocation();
+ LogUtil.start();
+ accessLogFilter.invoke(invoker, invocation);
+ assertEquals(1, LogUtil.findMessage("Exception in AcessLogFilter of service"));
+ LogUtil.stop();
+ }
+
+ // TODO how to assert thread action
+ @Test
+ public void testDefault() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1");
+ Invoker<AccessLogFilterTest> invoker = new MyInvoker<AccessLogFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ accessLogFilter.invoke(invoker, invocation);
+ }
+
+ @Test
+ public void testCustom() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=alibaba");
+ Invoker<AccessLogFilterTest> invoker = new MyInvoker<AccessLogFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ accessLogFilter.invoke(invoker, invocation);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilterTest.java
new file mode 100644
index 0000000..4fa5d68
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilterTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertNotSame;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MyInvoker;
+
+/**
+ * ActiveLimitFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class ActiveLimitFilterTest {
+
+ Filter activeLimitFilter = new ActiveLimitFilter();
+ private static volatile int count = 0;
+
+ @Test
+ public void testInvokeNoActives() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&actives=0");
+ Invoker<ActiveLimitFilterTest> invoker = new MyInvoker<ActiveLimitFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ activeLimitFilter.invoke(invoker, invocation);
+ }
+
+ @Test
+ public void testInvokeLessActives() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&actives=10");
+ Invoker<ActiveLimitFilterTest> invoker = new MyInvoker<ActiveLimitFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ activeLimitFilter.invoke(invoker, invocation);
+ }
+
+ @Test
+ public void testInvokeGreaterActives() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&actives=1&timeout=1");
+ final Invoker<ActiveLimitFilterTest> invoker = new MyInvoker<ActiveLimitFilterTest>(url);
+ final Invocation invocation = new MockInvocation();
+ for (int i = 0; i < 100; i++) {
+ Thread thread = new Thread(new Runnable() {
+
+ public void run() {
+ for (int i = 0; i < 100; i++) {
+ try {
+ activeLimitFilter.invoke(invoker, invocation);
+ } catch (RpcException expected) {
+ count++;
+ }
+ }
+ }
+ });
+ thread.start();
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ assertNotSame(0, count);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/CompatibleFilterFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/CompatibleFilterFilterTest.java
new file mode 100644
index 0000000..87532e7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/CompatibleFilterFilterTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.support.DemoService;
+import com.alibaba.dubbo.rpc.support.Type;
+
+/**
+ * CompatibleFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class CompatibleFilterFilterTest {
+
+ Filter compatibleFilter = new CompatibleFilter();
+ Invocation invocation;
+ Invoker<DemoService> invoker;
+
+ @After
+ public void tearDown() {
+ EasyMock.reset(invocation, invoker);
+ }
+
+ @Test
+ public void testInvokerGeneric() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("$enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setValue("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals(filterResult, result);
+ }
+
+ @Test
+ public void testResulthasException() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setException(new RuntimeException());
+ result.setValue("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals(filterResult, result);
+ }
+
+ @Test
+ public void testInvokerJsonPojoSerialization() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Type[].class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setValue("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&serialization=json");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals(Type.High, filterResult.getValue());
+ }
+
+ @Test
+ public void testInvokerNonJsonEnumSerialization() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Type[].class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setValue("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals(Type.High, filterResult.getValue());
+ }
+
+ @Test
+ public void testInvokerNonJsonNonPojoSerialization() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] {String.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setValue(new String[]{"High"});
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertArrayEquals(new String[]{"High"}, (String[])filterResult.getValue());
+ }
+
+ @Test
+ public void testInvokerNonJsonPojoSerialization() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { String.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setValue("hello");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals("hello", filterResult.getValue());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilterTest.java
new file mode 100644
index 0000000..136a673
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilterTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.support.DemoService;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MyInvoker;
+
+/**
+ * ConsumerContextFilterTest.java
+ * @author tony.chenl
+ */
+public class ConsumerContextFilterTest {
+ Filter consumerContextFilter = new ConsumerContextFilter();
+ @Test
+ public void testSetContext(){
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ Invoker<DemoService> invoker = new MyInvoker<DemoService>(url);
+ Invocation invocation = new MockInvocation();
+ consumerContextFilter.invoke(invoker, invocation);
+ assertEquals(invoker,RpcContext.getContext().getInvoker());
+ assertEquals(invocation,RpcContext.getContext().getInvocation());
+ assertEquals(NetUtils.getLocalHost() + ":0",RpcContext.getContext().getLocalAddressString());
+ assertEquals("test:11",RpcContext.getContext().getRemoteAddressString());
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ContextFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ContextFilterTest.java
new file mode 100644
index 0000000..3a80175
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ContextFilterTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertNull;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.support.DemoService;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MyInvoker;
+
+/**
+ * ContextFilterTest.java
+ * TODO 增强断言
+ * @author tony.chenl
+ */
+public class ContextFilterTest {
+
+ Filter contextFilter = new ContextFilter();
+ Invoker<DemoService> invoker;
+ Invocation invocation;
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testSetContext() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("$enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setValue("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ contextFilter.invoke(invoker, invocation);
+ assertNull(RpcContext.getContext().getInvoker());
+ }
+
+ @Test
+ public void testWithAttachments() {
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ Invoker<DemoService> invoker = new MyInvoker<DemoService>(url);
+ Invocation invocation = new MockInvocation();
+ Result result = contextFilter.invoke(invoker, invocation);
+ assertNull(RpcContext.getContext().getInvoker());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilterTest.java
new file mode 100644
index 0000000..1d36041
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilterTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.LogUtil;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.support.DemoService;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MyInvoker;
+
+/**
+ * DeprecatedFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class DeprecatedFilterTest {
+
+ Filter deprecatedFilter = new DeprecatedFilter();
+
+ @Test
+ public void testDeprecatedFilter() {
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&echo." + Constants.DEPRECATED_KEY + "=true");
+ LogUtil.start();
+ deprecatedFilter.invoke(new MyInvoker<DemoService>(url), new MockInvocation());
+ assertEquals(1,
+ LogUtil.findMessage("The service method com.alibaba.dubbo.rpc.support.DemoService.echo(String) is DEPRECATED"));
+ LogUtil.stop();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/EchoFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/EchoFilterTest.java
new file mode 100644
index 0000000..51c8a5a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/EchoFilterTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertEquals;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.support.DemoService;
+
+/**
+ * EchoFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class EchoFilterTest {
+
+ Filter echoFilter = new EchoFilter();
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testEcho() {
+ Invocation invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("$echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();
+ EasyMock.replay(invocation);
+ Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setValue("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = echoFilter.invoke(invoker, invocation);
+ assertEquals("hello", filterResult.getValue());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testNonEcho() {
+ Invocation invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();
+ EasyMock.replay(invocation);
+ Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setValue("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = echoFilter.invoke(invoker, invocation);
+ assertEquals("High", filterResult.getValue());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ExceptionFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/ExceptionFilterTest.java
new file mode 100644
index 0000000..3c93a80
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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 undeclared exception. service: " + DemoService.class.getName() + ", method: sayHello, exception: " + RpcException.class.getName() + ": 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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilterTest.java
new file mode 100644
index 0000000..99d8e61
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/filter/TpsLimitFilterTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcStatus;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MyInvoker;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ */
+public class TpsLimitFilterTest {
+
+ private TpsLimitFilter filter = new TpsLimitFilter();
+
+ @Test
+ public void testWithoutCount() throws Exception {
+ URL url = URL.valueOf("test://test");
+ url = url.addParameter(Constants.TPS_MAX_KEY, 5);
+ Invoker<TpsLimitFilterTest> invoker = new MyInvoker<TpsLimitFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ filter.invoke(invoker, invocation);
+ }
+
+ @Test(expected = RpcException.class)
+ public void testFail() throws Exception {
+ URL url = URL.valueOf("test://test");
+ url = url.addParameter(Constants.TPS_MAX_KEY, 5);
+ Invoker<TpsLimitFilterTest> invoker = new MyInvoker<TpsLimitFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ for (int i = 0; i < 10; i++) {
+ RpcStatus.beginCount(url, invocation.getMethodName());
+ Thread.sleep(100);
+ RpcStatus.endCount(url, invocation.getMethodName(), 100, true);
+ }
+ filter.invoke(invoker, invocation);
+ }
+
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoRequest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoRequest.java
new file mode 100644
index 0000000..9a2da25
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoService.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoService.java
new file mode 100644
index 0000000..917262a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoServiceImpl.java
new file mode 100644
index 0000000..de02c54
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteService.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteService.java
new file mode 100644
index 0000000..194b0c7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteServiceImpl.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteServiceImpl.java
new file mode 100644
index 0000000..5426978
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/Type.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/proxy/Type.java
new file mode 100644
index 0000000..ead26d1
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/DemoService.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/DemoService.java
new file mode 100644
index 0000000..f191ae8
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/DemoServiceImpl.java
new file mode 100644
index 0000000..8b602ee
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/IEcho.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/IEcho.java
new file mode 100644
index 0000000..4a63a5c
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.java
new file mode 100644
index 0000000..9910e82
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * MockInvocation.java
+ *
+ * @author tony.chenl
+ */
+public class MockInvocation implements Invocation {
+
+ public String getMethodName() {
+ return "echo";
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return new Class[] { String.class };
+ }
+
+ public Object[] getArguments() {
+ return new Object[] { "aa" };
+ }
+
+ public Map<String, String> getAttachments() {
+ Map<String, String> attachments = new HashMap<String, String>();
+ attachments.put(Constants.PATH_KEY, "dubbo");
+ attachments.put(Constants.GROUP_KEY, "dubbo");
+ attachments.put(Constants.VERSION_KEY, "1.0.0");
+ attachments.put(Constants.DUBBO_VERSION_KEY, "1.0.0");
+ attachments.put(Constants.TOKEN_KEY, "sfag");
+ attachments.put(Constants.TIMEOUT_KEY, "1000");
+ return attachments;
+ }
+
+ public Invoker<?> getInvoker() {
+ return null;
+ }
+
+ public String getAttachment(String key) {
+ return getAttachments().get(key);
+ }
+
+ public String getAttachment(String key, String defaultValue) {
+ return getAttachments().get(key);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/MyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/MyInvoker.java
new file mode 100644
index 0000000..2c2b6f8
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/MyInvoker.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * MockInvoker.java
+ *
+ * @author tony.chenl
+ */
+public class MyInvoker<T> implements Invoker<T> {
+
+ URL url;
+ Class<T> type;
+ boolean hasException = false;
+
+ public MyInvoker(URL url){
+ this.url = url;
+ type = (Class<T>) DemoService.class;
+ }
+
+ public MyInvoker(URL url, boolean hasException){
+ this.url = url;
+ type = (Class<T>) DemoService.class;
+ this.hasException = hasException;
+ }
+
+ public Class<T> getInterface() {
+ return type;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isAvailable() {
+ return false;
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ RpcResult result = new RpcResult();
+ if (hasException == false) {
+ result.setValue("alibaba");
+ return result;
+ } else {
+ result.setException(new RuntimeException("mocked exception"));
+ return result;
+ }
+
+ }
+
+ public void destroy() {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/Person.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/Person.java
new file mode 100644
index 0000000..19db863
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/RpcUtilsTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/RpcUtilsTest.java
new file mode 100644
index 0000000..74310bb
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/RpcUtilsTest.java
@@ -0,0 +1,83 @@
+package com.alibaba.dubbo.rpc.support;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+public class RpcUtilsTest {
+
+ /**
+ * 正常场景:url中表示了方法异步调用
+ * 验证:1. invocationId是否正常设置,2.幂等测试
+ */
+ @Test
+ public void testAttachInvocationIdIfAsync_normal() {
+ URL url = URL.valueOf("dubbo://localhost/?test.async=true");
+ Map<String,String> attachments = new HashMap<String,String>();
+ attachments.put("aa", "bb");
+ Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}, attachments);
+ RpcUtils.attachInvocationIdIfAsync(url, inv);
+ long id1 = RpcUtils.getInvocationId(inv);
+ RpcUtils.attachInvocationIdIfAsync(url, inv);
+ long id2 = RpcUtils.getInvocationId(inv);
+ Assert.assertTrue( id1 == id2); //幂等操作验证
+ Assert.assertTrue( id1 >= 0);
+ Assert.assertEquals("bb", attachments.get("aa"));
+ }
+
+ /**
+ * 场景:同步调用,不默认添加acctachment
+ * 验证:acctachment中没有添加id属性
+ */
+ @Test
+ public void testAttachInvocationIdIfAsync_sync() {
+ URL url = URL.valueOf("dubbo://localhost/");
+ Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{});
+ RpcUtils.attachInvocationIdIfAsync(url, inv);
+ Assert.assertNull(RpcUtils.getInvocationId(inv));
+ }
+
+ /**
+ * 场景:异步调用,默认添加attachement
+ * 验证:当原始acctachment为null时,不能报错.
+ */
+ @Test
+ public void testAttachInvocationIdIfAsync_nullAttachments() {
+ URL url = URL.valueOf("dubbo://localhost/?test.async=true");
+ Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{});
+ RpcUtils.attachInvocationIdIfAsync(url, inv);
+ Assert.assertTrue(RpcUtils.getInvocationId(inv) >= 0l);
+ }
+
+ /**
+ * 场景:强制设置为不添加
+ * 验证:acctachment中没有添加id属性
+ */
+ @Test
+ public void testAttachInvocationIdIfAsync_forceNotAttache() {
+ URL url = URL.valueOf("dubbo://localhost/?test.async=true&"+Constants.AUTO_ATTACH_INVOCATIONID_KEY+"=false");
+ Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{});
+ RpcUtils.attachInvocationIdIfAsync(url, inv);
+ Assert.assertNull(RpcUtils.getInvocationId(inv));
+ }
+
+ /**
+ * 场景:强制设置为添加
+ * 验证:acctachment中有添加id属性
+ */
+ @Test
+ public void testAttachInvocationIdIfAsync_forceAttache() {
+ URL url = URL.valueOf("dubbo://localhost/?"+Constants.AUTO_ATTACH_INVOCATIONID_KEY+"=true");
+ Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{});
+ RpcUtils.attachInvocationIdIfAsync(url, inv);
+ Assert.assertNotNull(RpcUtils.getInvocationId(inv));
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/Type.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/com/alibaba/dubbo/rpc/support/Type.java
new file mode 100644
index 0000000..ed1dd9a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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/dubbo-rpc-api/src/test/resources/log4j.xml b/dubbo-rpc/dubbo-rpc-api/src/test/resources/log4j.xml
new file mode 100644
index 0000000..e6c81db
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/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-rpc/dubbo-rpc-default/pom.xml b/dubbo-rpc/dubbo-rpc-default/pom.xml
new file mode 100644
index 0000000..01ef404
--- /dev/null
+++ b/dubbo-rpc/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-rpc</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-rpc-default</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-container-api</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/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
new file mode 100644
index 0000000..cb5c5a5
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+/**
+ * callback 服务帮助类.
+ * @author chao.liuc
+ *
+ */
+class CallbackServiceCodec {
+ private static final Logger logger = LoggerFactory.getLogger(CallbackServiceCodec.class);
+
+ private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ private static final DubboProtocol protocol = DubboProtocol.getDubboProtocol();
+ private static final byte CALLBACK_NONE = 0x0;
+ private static final byte CALLBACK_CREATE = 0x1;
+ private static final byte CALLBACK_DESTROY = 0x2;
+ private static final String INV_ATT_CALLBACK_KEY = "sys_callback_arg-";
+
+ private static byte isCallBack(URL url, String methodName ,int argIndex){
+ //参数callback的规则是 方法名称.参数index(0开始).callback
+ byte isCallback = CALLBACK_NONE;
+ if (url != null ) {
+ String callback = url.getParameter(methodName+"."+argIndex+".callback");
+ if(callback != null) {
+ if (callback.equalsIgnoreCase("true")) {
+ isCallback = CALLBACK_CREATE;
+ }else if(callback.equalsIgnoreCase("false")){
+ isCallback = CALLBACK_DESTROY;
+ }
+ }
+ }
+ return isCallback;
+ }
+
+ /**
+ * client 端export callback service
+ * @param channel
+ * @param clazz
+ * @param inst
+ * @param export
+ * @param out
+ * @throws IOException
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static String exportOrunexportCallbackService(Channel channel, URL url, Class clazz, Object inst, Boolean export) throws IOException{
+ int instid = System.identityHashCode(inst);
+
+ Map<String,String> params = new HashMap<String,String>(3);
+ //不需要在重新new client
+ params.put(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
+ //标识callback 变于排查问题
+ params.put(Constants.IS_CALLBACK_SERVICE, Boolean.TRUE.toString());
+ String group = url.getParameter(Constants.GROUP_KEY);
+ if (group != null && group.length() > 0){
+ params.put(Constants.GROUP_KEY,group);
+ }
+ //增加方法,变于方法检查,自动降级(见dubbo protocol)
+ params.put(Constants.METHODS_KEY, StringUtils.join(Wrapper.getWrapper(clazz).getDeclaredMethodNames(), ","));
+
+ Map<String, String> tmpmap = new HashMap<String, String>(url.getParameters());
+ tmpmap.putAll(params);
+ tmpmap.remove(Constants.VERSION_KEY);//callback不需要区分version
+ tmpmap.put(Constants.INTERFACE_KEY, clazz.getName());
+ 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 ){
+ URL referurl = URL.valueOf("callback://" + url.getAddress() + "/" + clazz.getName() + "?" + Constants.INTERFACE_KEY + "=" + clazz.getName());
+ referurl = referurl.addParametersIfAbsent(url.getParameters()).removeParameter(Constants.METHODS_KEY);
+ if (!isInstancesOverLimit(channel, referurl, clazz.getName(), instid, true)){
+ @SuppressWarnings("rawtypes")
+ Invoker<?> invoker = new ChannelWrappedInvoker(clazz, channel, referurl, String.valueOf(instid));
+ proxy = proxyFactory.getProxy(invoker);
+ channel.setAttribute(proxyCacheKey, proxy);
+ channel.setAttribute(invokerCacheKey, invoker);
+ increaseInstanceCount(channel, countkey);
+
+ //convert error fail fast .
+ //ignore concurrent problem.
+ Set<Invoker<?>> callbackInvokers = (Set<Invoker<?>>)channel.getAttribute(Constants.CHANNEL_CALLBACK_KEY);
+ if (callbackInvokers == null){
+ callbackInvokers = new ConcurrentHashSet<Invoker<?>>(1);
+ callbackInvokers.add(invoker);
+ channel.setAttribute(Constants.CHANNEL_CALLBACK_KEY, callbackInvokers);
+ }
+ logger.info ("method "+inv.getMethodName()+" include a callback service :"+invoker.getUrl() +", a proxy :"+invoker +" has been created.") ;
+ }
+ }
+ } else {
+ if(proxy != null){
+ Invoker<?> invoker = (Invoker<?>)channel.getAttribute(invokerCacheKey);
+ try{
+ Set<Invoker<?>> callbackInvokers = (Set<Invoker<?>>)channel.getAttribute(Constants.CHANNEL_CALLBACK_KEY);
+ if (callbackInvokers != null ) {
+ callbackInvokers.remove(invoker);
+ }
+ invoker.destroy();
+ }catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ //取消refer 直接在map中去除,
+ channel.removeAttribute(proxyCacheKey);
+ channel.removeAttribute(invokerCacheKey);
+ decreaseInstanceCount(channel,countkey);
+ }
+ }
+ return proxy;
+ }
+
+ private static String getClientSideCallbackServiceCacheKey(int instid){
+ return Constants.CALLBACK_SERVICE_KEY+"."+instid;
+ }
+ private static String getServerSideCallbackServiceCacheKey(Channel channel, String interfaceClass, int instid){
+ return Constants.CALLBACK_SERVICE_PROXY_KEY+"."+System.identityHashCode(channel)+"."+ interfaceClass +"."+instid;
+ }
+ private static String getServerSideCallbackInvokerCacheKey(Channel channel, String interfaceClass, int instid){
+ return getServerSideCallbackServiceCacheKey(channel, interfaceClass, instid) + "." + "invoker";
+ }
+
+ private static String getClientSideCountKey(String interfaceClass){
+ return Constants.CALLBACK_SERVICE_KEY+"."+interfaceClass+".COUNT";
+ }
+ private static String getServerSideCountKey(Channel channel, String interfaceClass){
+ return Constants.CALLBACK_SERVICE_PROXY_KEY+"."+System.identityHashCode(channel)+"."+interfaceClass+".COUNT";
+ }
+ private static boolean isInstancesOverLimit(Channel channel, URL url ,String interfaceClass, int instid, boolean isServer){
+ Integer count = (Integer)channel.getAttribute(isServer ? getServerSideCountKey(channel,interfaceClass) : getClientSideCountKey(interfaceClass));
+ int limit = url.getParameter(Constants.CALLBACK_INSTANCES_LIMIT_KEY, Constants.DEFAULT_CALLBACK_INSTANCES);
+ if (count != null && count >= limit){
+ //client side error
+ throw new IllegalStateException("interface " + interfaceClass +" `s callback instances num exceed providers limit :"+ limit
+ +" ,current num: "+(count+1)+". The new callback service will not work !!! you can cancle the callback service which exported before. channel :"+ channel);
+ }else {
+ return false;
+ }
+ }
+ private static void increaseInstanceCount(Channel channel, String countkey){
+ try{
+ //ignore cuncurrent problem?
+ Integer count = (Integer)channel.getAttribute(countkey);
+ if (count == null ){
+ count = 1;
+ }else {
+ count ++ ;
+ }
+ channel.setAttribute(countkey, count);
+ }catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ private static void decreaseInstanceCount(Channel channel, String countkey){
+ try{
+ Integer count = (Integer)channel.getAttribute(countkey);
+ if (count == null || count <= 0){
+ return;
+ }else {
+ count -- ;
+ }
+ channel.setAttribute(countkey, count);
+ }catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+ public static Object encodeInvocationArgument(Channel channel, RpcInvocation inv, int paraIndex) throws IOException{
+ //encode时可直接获取url
+ URL url = inv.getInvoker() == null ? null : inv.getInvoker().getUrl();
+ byte callbackstatus = isCallBack(url, inv.getMethodName(), paraIndex);
+ Object[] args = inv.getArguments();
+ Class<?>[] pts = inv.getParameterTypes();
+ switch (callbackstatus) {
+ case CallbackServiceCodec.CALLBACK_NONE:
+ return args[paraIndex];
+ case CallbackServiceCodec.CALLBACK_CREATE:
+ inv.setAttachment(INV_ATT_CALLBACK_KEY + paraIndex , exportOrunexportCallbackService(channel, url, pts[paraIndex], args[paraIndex], true));
+ return null;
+ case CallbackServiceCodec.CALLBACK_DESTROY:
+ inv.setAttachment(INV_ATT_CALLBACK_KEY + paraIndex, exportOrunexportCallbackService(channel, url, pts[paraIndex], args[paraIndex], false));
+ return null;
+ default:
+ return args[paraIndex];
+ }
+ }
+ public static Object decodeInvocationArgument(Channel channel, RpcInvocation inv, Class<?>[] pts, int paraIndex, Object inObject) throws IOException{
+ //如果是callback,则创建proxy到客户端,方法的执行可通过channel调用到client端的callback接口
+ //decode时需要根据channel及env获取url
+ URL url = null ;
+ try {
+ url = DubboProtocol.getDubboProtocol().getInvoker(channel, inv).getUrl();
+ } catch (RemotingException e) {
+ if (logger.isInfoEnabled()) {
+ logger.info(e.getMessage(), e);
+ }
+ return inObject;
+ }
+ byte callbackstatus = isCallBack(url, inv.getMethodName(), paraIndex);
+ switch (callbackstatus) {
+ case CallbackServiceCodec.CALLBACK_NONE:
+ return inObject;
+ case CallbackServiceCodec.CALLBACK_CREATE:
+ try{
+ return referOrdestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), true);
+ }catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ throw new IOException(StringUtils.toString(e));
+ }
+ case CallbackServiceCodec.CALLBACK_DESTROY:
+ try{
+ return referOrdestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), false);
+ }catch (Exception e) {
+ throw new IOException(StringUtils.toString(e));
+ }
+ default:
+ return inObject ;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java
new file mode 100644
index 0000000..e87d0be
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.TimeoutException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient;
+import com.alibaba.dubbo.remoting.transport.ClientDelegate;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+
+/**
+ * 基于已有channel的invoker.
+ *
+ * @author chao.liuc
+ */
+class ChannelWrappedInvoker<T> extends AbstractInvoker<T> {
+
+ private final Channel channel;
+ private final String serviceKey ;
+
+ public ChannelWrappedInvoker(Class<T> serviceType, Channel channel, URL url, String serviceKey) {
+
+ super(serviceType, url, new String[] { Constants.GROUP_KEY,
+ Constants.TOKEN_KEY, Constants.TIMEOUT_KEY });
+ this.channel = channel;
+ this.serviceKey = serviceKey;
+ }
+
+ @Override
+ protected Result doInvoke(Invocation invocation) throws Throwable {
+ RpcInvocation inv = new RpcInvocation(invocation);
+ //拿不到client端export 的service path.约定为interface的名称.
+ inv.setAttachment(Constants.PATH_KEY, getInterface().getName());
+ inv.setAttachment(Constants.CALLBACK_SERVICE_KEY, serviceKey);
+
+ ExchangeClient currentClient = new HeaderExchangeClient(new ChannelWrapper(this.channel));
+
+ try {
+ if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) { // 不可靠异步
+ currentClient.send(inv,getUrl().getMethodParameter(invocation.getMethodName(), Constants.SENT_KEY, false));
+ return new RpcResult();
+ }
+ int timeout = getUrl().getMethodParameter(invocation.getMethodName(),
+ Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ if (timeout > 0) {
+ return (Result) currentClient.request(inv, timeout).get();
+ } else {
+ return (Result) currentClient.request(inv).get();
+ }
+ } catch (RpcException e) {
+ throw e;
+ } catch (TimeoutException e) {
+ throw new RpcException(RpcException.TIMEOUT_EXCEPTION, e.getMessage(), e);
+ } catch (RemotingException e) {
+ throw new RpcException(RpcException.NETWORK_EXCEPTION, e.getMessage(), e);
+ } catch (Throwable e) { // here is non-biz exception, wrap it.
+ throw new RpcException(e.getMessage(), e);
+ }
+ }
+
+ public static class ChannelWrapper extends ClientDelegate {
+
+ private final Channel channel;
+ private final URL url;
+
+ public ChannelWrapper(Channel channel) {
+ this.channel = channel;
+ this.url = channel.getUrl().addParameter("codec", DubboCodec.NAME);
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return channel.getChannelHandler();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return channel.getLocalAddress();
+ }
+
+ public void close() {
+ channel.close();
+ }
+
+ public boolean isClosed() {
+ return channel == null ? true : channel.isClosed();
+ }
+
+ public void reset(URL url) {
+ throw new RpcException("ChannelInvoker can not reset.");
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return channel.getLocalAddress();
+ }
+
+ public boolean isConnected() {
+ return channel == null ? false : channel.isConnected();
+ }
+
+ public boolean hasAttribute(String key) {
+ return channel.hasAttribute(key);
+ }
+
+ public Object getAttribute(String key) {
+ return channel.getAttribute(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ channel.setAttribute(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ channel.removeAttribute(key);
+ }
+
+ public void reconnect() throws RemotingException {
+
+ }
+
+ public void send(Object message) throws RemotingException {
+ channel.send(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ channel.send(message, sent);
+ }
+
+ }
+
+ public void destroy() {
+ //channel资源的清空由channel创建者清除.
+// super.destroy();
+// try {
+// channel.close();
+// } catch (Throwable t) {
+// logger.warn(t.getMessage(), t);
+// }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java
new file mode 100644
index 0000000..dcb67be
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import static com.alibaba.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.decodeInvocationArgument;
+import static com.alibaba.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.encodeInvocationArgument;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.support.RpcUtils;
+
+/**
+ * Dubbo codec.
+ *
+ * @author qianlei
+ * @author chao.liuc
+ */
+public class DubboCodec extends ExchangeCodec implements Codec {
+
+ public static final String NAME = "dubbo";
+
+ private static final String DUBBO_VERSION = Version.getVersion(DubboCodec.class, Version.getVersion());
+
+ private static final byte RESPONSE_WITH_EXCEPTION = 0;
+
+ private static final byte RESPONSE_VALUE = 1;
+
+ private static final byte RESPONSE_NULL_VALUE = 2;
+
+ private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
+ private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
+
+
+ @Override
+ protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ RpcInvocation inv = (RpcInvocation) data;
+
+ out.writeUTF(inv.getAttachment(Constants.DUBBO_VERSION_KEY, DUBBO_VERSION));
+ out.writeUTF(inv.getAttachment(Constants.PATH_KEY));
+ out.writeUTF(inv.getAttachment(Constants.VERSION_KEY));
+
+ out.writeUTF(inv.getMethodName());
+ out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes()));
+ Object[] args = inv.getArguments();
+ if (args != null)
+ for (int i = 0; i < args.length; i++){
+ out.writeObject(encodeInvocationArgument(channel, inv, i));
+ }
+ out.writeObject(inv.getAttachments());
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException {
+ RpcInvocation inv = new RpcInvocation();
+
+ inv.setAttachment(Constants.DUBBO_VERSION_KEY, in.readUTF());
+ inv.setAttachment(Constants.PATH_KEY, in.readUTF());
+ inv.setAttachment(Constants.VERSION_KEY, in.readUTF());
+
+ inv.setMethodName(in.readUTF());
+ try {
+ Object[] args;
+ Class<?>[] pts;
+ String desc = in.readUTF();
+ if (desc.length() == 0) {
+ pts = EMPTY_CLASS_ARRAY;
+ args = EMPTY_OBJECT_ARRAY;
+ } else {
+ pts = ReflectUtils.desc2classArray(desc);
+ args = new Object[pts.length];
+ for (int i = 0; i < args.length; i++){
+ try{
+ args[i] = in.readObject(pts[i]);
+ }catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ inv.setParameterTypes(pts);
+
+ Map<String, String> map = (Map<String, String>) in.readObject(Map.class);
+ if (map != null && map.size() > 0) {
+ Map<String, String> attachment = inv.getAttachments();
+ if (attachment == null) {
+ attachment = new HashMap<String, String>();
+ }
+ attachment.putAll(map);
+ inv.setAttachments(attachment);
+ }
+ //decode argument ,may be callback
+ for (int i = 0; i < args.length; i++){
+ args[i] = decodeInvocationArgument(channel, inv, pts, i, args[i]);
+ }
+
+ inv.setArguments(args);
+
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read invocation data failed.", e));
+ }
+ return inv;
+ }
+
+ @Override
+ protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ Result result = (Result) data;
+
+ Throwable th = result.getException();
+ if (th == null) {
+ Object ret = result.getValue();
+ if (ret == null) {
+ out.writeByte(RESPONSE_NULL_VALUE);
+ } else {
+ out.writeByte(RESPONSE_VALUE);
+ out.writeObject(ret);
+ }
+ } else {
+ out.writeByte(RESPONSE_WITH_EXCEPTION);
+ out.writeObject(th);
+ }
+ }
+
+ @Override
+ protected Object decodeResponseData(Channel channel, ObjectInput in, Object request) throws IOException {
+ Invocation invocation = (Invocation) request;
+ RpcResult result = new RpcResult();
+
+ byte flag = in.readByte();
+ switch (flag) {
+ case RESPONSE_NULL_VALUE:
+ break;
+ case RESPONSE_VALUE:
+ try {
+ Type[] returnType = RpcUtils.getReturnTypes(invocation);
+ result.setValue(returnType == null || returnType.length == 0 ? in.readObject() :
+ (returnType.length == 1 ? in.readObject((Class<?>)returnType[0])
+ : in.readObject((Class<?>)returnType[0], returnType[1])));
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read response data failed.", e));
+ }
+ break;
+ case RESPONSE_WITH_EXCEPTION:
+ try {
+ Object obj = in.readObject();
+ if (obj instanceof Throwable == false) throw new IOException("Response data error, expect Throwable, but get " + obj);
+ result.setException((Throwable) obj);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read response data failed.", e));
+ }
+ break;
+ default:
+ throw new IOException("Unknown result flag, expect '0' '1' '2', get " + flag);
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboExporter.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.java
new file mode 100644
index 0000000..101f2fd
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.AtomicPositiveInteger;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.TimeoutException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+import com.alibaba.dubbo.rpc.support.RpcUtils;
+
+/**
+ * DubboInvoker
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class DubboInvoker<T> extends AbstractInvoker<T> {
+
+ private final ExchangeClient[] clients;
+
+ private final AtomicPositiveInteger index = new AtomicPositiveInteger();
+
+ private final String version;
+
+ private final ReentrantLock destroyLock = new ReentrantLock();
+
+ private final Set<Invoker<?>> invokers;
+
+ public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients){
+ this(serviceType, url, clients, null);
+ }
+
+ public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers){
+ super(serviceType, url, new String[] {Constants.INTERFACE_KEY, Constants.GROUP_KEY, Constants.TOKEN_KEY, Constants.TIMEOUT_KEY});
+ this.clients = clients;
+ // get version.
+ this.version = url.getParameter(Constants.VERSION_KEY, "0.0.0");
+ this.invokers = invokers;
+ }
+
+ @Override
+ protected Result doInvoke(final Invocation invocation) throws Throwable {
+ RpcInvocation inv = null;
+ final String methodName ;
+ if(Constants.$INVOKE.equals(invocation.getMethodName())
+ && invocation.getArguments() != null
+ && invocation.getArguments().length >0
+ && invocation.getArguments()[0] != null){
+ inv = (RpcInvocation) invocation;
+ //the frist argument must be real method name;
+ methodName = invocation.getArguments()[0].toString();
+ }else {
+ inv = new RpcInvocation(invocation);
+ methodName = invocation.getMethodName();
+ }
+ inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
+ inv.setAttachment(Constants.VERSION_KEY, version);
+
+ ExchangeClient currentClient;
+ if (clients.length == 1) {
+ currentClient = clients[0];
+ } else {
+ currentClient = clients[index.getAndIncrement() % clients.length];
+ }
+ try {
+ boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
+ boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
+ int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
+ if (isOneway) {
+ boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
+ currentClient.send(inv, isSent);
+ RpcContext.getContext().setFuture(null);
+ return new RpcResult();
+ } else if (isAsync) {
+ ResponseFuture future = currentClient.request(inv, timeout) ;
+ RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
+ return new RpcResult();
+ } else {
+ RpcContext.getContext().setFuture(null);
+ return (Result) currentClient.request(inv, timeout).get();
+ }
+ } catch (TimeoutException e) {
+ throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
+ } catch (RemotingException e) {
+ throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable())
+ return false;
+ for (ExchangeClient client : clients){
+ if (client.isConnected() && !client.hasAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY)){
+ //cannot write == not Available ?
+ return true ;
+ }
+ }
+ return false;
+ }
+
+ public void destroy() {
+ //防止client被关闭多次.在connect per jvm的情况下,client.close方法会调用计数器-1,当计数器小于等于0的情况下,才真正关闭
+ if (super.isDestroyed()){
+ return ;
+ } else {
+ //dubbo check ,避免多次关闭
+ destroyLock.lock();
+ try{
+ if (super.isDestroyed()){
+ return ;
+ }
+ super.destroy();
+ if (invokers != null){
+ invokers.remove(this);
+ }
+ for (ExchangeClient client : clients) {
+ try {
+ client.close();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+
+ }finally {
+ destroyLock.unlock();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java
new file mode 100644
index 0000000..2359cb6
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Transporter;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;
+
+/**
+ * dubbo protocol support.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class DubboProtocol extends AbstractProtocol {
+
+ public static final String NAME = "dubbo";
+
+ public static final String COMPATIBLE_CODEC_NAME = "dubbo1compatible";
+
+ public static final int DEFAULT_PORT = 20880;
+
+ public final ReentrantLock lock = new ReentrantLock();
+
+ private final Map<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>(); // <host:port,Exchanger>
+
+ private final Map<String, ReferenceCountExchangeClient> referenceClientMap = new ConcurrentHashMap<String, ReferenceCountExchangeClient>(); // <host:port,Exchanger>
+
+ private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap = new ConcurrentHashMap<String, LazyConnectExchangeClient>();
+
+ //consumer side export a stub service for dispatching event
+ //servicekey-stubmethods
+ private final ConcurrentMap<String, String> stubServiceMethodsMap = new ConcurrentHashMap<String, String>();
+
+ private static final String IS_CALLBACK_SERVICE_INVOKE = "_isCallBackServiceInvoke";
+
+ private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
+
+ public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
+ if (message instanceof Invocation) {
+ Invocation inv = (Invocation) message;
+ Invoker<?> invoker = getInvoker(channel, inv);
+ //如果是callback 需要处理高版本调用低版本的问题
+ if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))){
+ String methodsStr = invoker.getUrl().getParameters().get("methods");
+ boolean hasMethod = false;
+ if (methodsStr == null || methodsStr.indexOf(",") == -1){
+ hasMethod = inv.getMethodName().equals(methodsStr);
+ } else {
+ String[] methods = methodsStr.split(",");
+ for (String method : methods){
+ if (inv.getMethodName().equals(method)){
+ hasMethod = true;
+ break;
+ }
+ }
+ }
+ if (!hasMethod){
+ logger.warn(new IllegalStateException("The methodName "+inv.getMethodName()+" not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) +" ,invocation is :"+inv );
+ return null;
+ }
+ }
+ RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
+ return invoker.invoke(inv);
+ }
+ throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
+ }
+
+ @Override
+ public void received(Channel channel, Object message) throws RemotingException {
+ if (message instanceof Invocation) {
+ reply((ExchangeChannel) channel, message);
+ } else {
+ super.received(channel, message);
+ }
+ }
+
+ @Override
+ public void connected(Channel channel) throws RemotingException {
+ invoke(channel, Constants.ON_CONNECT_KEY);
+ }
+
+ @Override
+ public void disconnected(Channel channel) throws RemotingException {
+ if(logger.isInfoEnabled()){
+ logger.info("disconected from "+ channel.getRemoteAddress() + ",url:" + channel.getUrl());
+ }
+ invoke(channel, Constants.ON_DISCONNECT_KEY);
+ }
+
+ private void invoke(Channel channel, String methodKey) {
+ Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
+ if (invocation != null) {
+ try {
+ received(channel, invocation);
+ } catch (Throwable t) {
+ logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
+ }
+ }
+ }
+
+ private Invocation createInvocation(Channel channel, URL url, String methodKey) {
+ String method = url.getParameter(methodKey);
+ if (method == null || method.length() == 0) {
+ return null;
+ }
+ RpcInvocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);
+ invocation.setAttachment(Constants.PATH_KEY, url.getPath());
+ invocation.setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));
+ invocation.setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));
+ invocation.setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY));
+ if (url.getParameter(Constants.STUB_EVENT_KEY, false)){
+ invocation.setAttachment(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString());
+ }
+ return invocation;
+ }
+ };
+
+ private static DubboProtocol INSTANCE;
+
+ public DubboProtocol() {
+ INSTANCE = this;
+ }
+
+ public static DubboProtocol getDubboProtocol() {
+ if (INSTANCE == null) {
+ ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(DubboProtocol.NAME); // load
+ }
+ return INSTANCE;
+ }
+
+ public Collection<ExchangeServer> getServers() {
+ return Collections.unmodifiableCollection(serverMap.values());
+ }
+
+ public Collection<Exporter<?>> getExporters() {
+ return Collections.unmodifiableCollection(exporterMap.values());
+ }
+
+ Map<String, Exporter<?>> getExporterMap(){
+ return exporterMap;
+ }
+
+ private boolean isClientSide(Channel channel) {
+ InetSocketAddress address = channel.getRemoteAddress();
+ URL url = channel.getUrl();
+ return url.getPort() == address.getPort() &&
+ NetUtils.filterLocalHost(channel.getUrl().getIp())
+ .equals(NetUtils.filterLocalHost(address.getAddress().getHostAddress()));
+ }
+
+ Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException{
+ boolean isCallBackServiceInvoke = false;
+ boolean isStubServiceInvoke = false;
+ int port = channel.getLocalAddress().getPort();
+ String path = inv.getAttachments().get(Constants.PATH_KEY);
+ //如果是客户端的回调服务.
+ isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getAttachments().get(Constants.STUB_EVENT_KEY));
+ if (isStubServiceInvoke){
+ port = channel.getRemoteAddress().getPort();
+ }
+ //callback
+ isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke;
+ if(isCallBackServiceInvoke){
+ path = inv.getAttachments().get(Constants.PATH_KEY)+"."+inv.getAttachments().get(Constants.CALLBACK_SERVICE_KEY);
+ inv.getAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString());
+ }
+ String serviceKey = serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.GROUP_KEY));
+
+ DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);
+
+ if (exporter == null)
+ throw new RemotingException(channel, "Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + inv);
+
+ return exporter.getInvoker();
+ }
+
+ public Collection<Invoker<?>> getInvokers() {
+ return Collections.unmodifiableCollection(invokers);
+ }
+
+ public int getDefaultPort() {
+ return DEFAULT_PORT;
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ URL url = invoker.getUrl().addParameterIfAbsent(Constants.DOWNSTREAM_CODEC_KEY, DubboCodec.NAME);
+
+ // export service.
+ String key = serviceKey(url);
+ DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
+ exporterMap.put(key, exporter);
+
+ //export an stub service for dispaching event
+ Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY,Constants.DEFAULT_STUB_EVENT);
+ Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
+ if (isStubSupportEvent && !isCallbackservice){
+ String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
+ if (stubServiceMethods == null || stubServiceMethods.length() == 0 ){
+ if (logger.isWarnEnabled()){
+ logger.warn( new IllegalStateException("consumer ["+url.getParameter(Constants.INTERFACE_KEY)+"], has set stubproxy support event ,but no stub methods founded."));
+ }
+ } else {
+ stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
+ }
+ }
+
+ openServer(url);
+
+ return exporter;
+ }
+
+ private void openServer(URL url) {
+ // find server.
+ String key = url.getAddress();
+ //client 也可以暴露一个只有server可以调用的服务。
+ boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
+ if (isServer) {
+ ExchangeServer server = serverMap.get(key);
+ if (server == null) {
+ serverMap.put(key, getServer(url));
+ } else {
+ //server支持reset,配合override功能使用
+ server.reset(url);
+ }
+ }
+ }
+
+ private ExchangeServer getServer(URL url) {
+ //默认开启server关闭时发送readonly事件
+ url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
+ //默认开启heartbeat
+ url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
+ String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
+
+ if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
+ throw new RpcException("Unsupported server type: " + str + ", url: " + url);
+
+ url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
+ ExchangeServer server;
+ try {
+ server = Exchangers.bind(url, requestHandler);
+ } catch (RemotingException e) {
+ throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
+ }
+ str = url.getParameter(Constants.CLIENT_KEY);
+ if (str != null && str.length() > 0) {
+ Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
+ if (!supportedTypes.contains(str)) {
+ throw new RpcException("Unsupported client type: " + str);
+ }
+ }
+ return server;
+ }
+
+ public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
+ // create rpc invoker.
+ DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
+ invokers.add(invoker);
+ return invoker;
+ }
+
+ private ExchangeClient[] getClients(URL url){
+ //是否共享连接
+ boolean service_share_connect = false;
+ int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
+ //如果connections不配置,则共享连接,否则每服务每连接
+ if (connections == 0){
+ service_share_connect = true;
+ connections = 1;
+ }
+
+ ExchangeClient[] clients = new ExchangeClient[connections];
+ for (int i = 0; i < clients.length; i++) {
+ if (service_share_connect){
+ clients[i] = getSharedClient(url);
+ } else {
+ clients[i] = initClient(url);
+ }
+ }
+ return clients;
+ }
+
+ /**
+ *获取共享连接
+ */
+ private ExchangeClient getSharedClient(URL url){
+ String key = url.getAddress();
+ ReferenceCountExchangeClient client = referenceClientMap.get(key);
+ if ( client != null ){
+ if ( !client.isClosed()){
+ client.incrementAndGetCount();
+ return client;
+ } else {
+// logger.warn(new IllegalStateException("client is closed,but stay in clientmap .client :"+ client));
+ referenceClientMap.remove(key);
+ }
+ }
+ ExchangeClient exchagneclient = initClient(url);
+
+ client = new ReferenceCountExchangeClient(exchagneclient, ghostClientMap);
+ referenceClientMap.put(key, client);
+ ghostClientMap.remove(key);
+ return client;
+ }
+
+ /**
+ * 创建新连接.
+ */
+ private ExchangeClient initClient(URL url) {
+
+ // client type setting.
+ String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
+
+ String version = url.getParameter(Constants.DUBBO_VERSION_KEY);
+ boolean compatible = (version != null && version.startsWith("1.0."));
+ url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() && compatible ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
+ //默认开启heartbeat
+ url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
+
+ // BIO存在严重性能问题,暂时不允许使用
+ if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
+ throw new RpcException("Unsupported client type: " + str + "," +
+ " supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
+ }
+
+ ExchangeClient client ;
+ try {
+ //设置连接应该是lazy的
+ if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)){
+ client = new LazyConnectExchangeClient(url ,requestHandler);
+ } else {
+ client = Exchangers.connect(url ,requestHandler);
+ }
+ } catch (RemotingException e) {
+ throw new RpcException("Fail to create remoting client for service(" + url
+ + "): " + e.getMessage(), e);
+ }
+ return client;
+ }
+
+ public void destroy() {
+ super.destroy();
+ for (String key : new ArrayList<String>(serverMap.keySet())) {
+ ExchangeServer server = serverMap.remove(key);
+ if (server != null) {
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Close dubbo server: " + server.getLocalAddress());
+ }
+ server.close(getServerShutdownTimeout());
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+
+ for (String key : new ArrayList<String>(referenceClientMap.keySet())) {
+ ExchangeClient client = referenceClientMap.remove(key);
+ if (client != null) {
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress());
+ }
+ client.close();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+
+ for (String key : new ArrayList<String>(ghostClientMap.keySet())) {
+ ExchangeClient client = ghostClientMap.remove(key);
+ if (client != null) {
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress());
+ }
+ client.close();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ stubServiceMethodsMap.clear();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java
new file mode 100644
index 0000000..dd27a53
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Parameters;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+
+/**
+ * dubbo protocol support class.
+ *
+ * @author chao.liuc
+ */
+@SuppressWarnings("deprecation")
+final class LazyConnectExchangeClient implements ExchangeClient{
+
+ private final static Logger logger = LoggerFactory.getLogger(LazyConnectExchangeClient.class);
+
+ private final URL url;
+ private final ExchangeHandler requestHandler;
+ private volatile ExchangeClient client;
+ private final Lock connectLock = new ReentrantLock();
+ //lazy connect 如果没有初始化时的连接状态
+ private final boolean initialState ;
+
+ protected final boolean requestWithWarning;
+
+ //当调用时warning,出现这个warning,表示程序可能存在bug.
+ static final String REQUEST_WITH_WARNING_KEY = "lazyclient_request_with_warning";
+
+ private AtomicLong warningcount = new AtomicLong(0);
+
+ public LazyConnectExchangeClient(URL url, ExchangeHandler requestHandler) {
+ //lazy connect ,need set send.reconnect = true, to avoid channel bad status.
+ this.url = url.addParameter(Constants.SEND_RECONNECT_KEY, Boolean.TRUE.toString());
+ this.requestHandler = requestHandler;
+ this.initialState = url.getParameter(Constants.LAZY_CONNECT_INITIAL_STATE_KEY,Constants.DEFAULT_LAZY_CONNECT_INITIAL_STATE);
+ this.requestWithWarning = url.getParameter(REQUEST_WITH_WARNING_KEY, false);
+ }
+
+
+ private void initClient() throws RemotingException {
+ if (client != null )
+ return;
+ if (logger.isInfoEnabled()) {
+ logger.info("Lazy connect to " + url);
+ }
+ connectLock.lock();
+ try {
+ if (client != null)
+ return;
+ this.client = Exchangers.connect(url, requestHandler);
+ } finally {
+ connectLock.unlock();
+ }
+ }
+
+ public ResponseFuture request(Object request) throws RemotingException {
+ warning(request);
+ initClient();
+ return client.request(request);
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ if (client == null){
+ return InetSocketAddress.createUnresolved(url.getHost(), url.getPort());
+ } else {
+ return client.getRemoteAddress();
+ }
+ }
+ public ResponseFuture request(Object request, int timeout) throws RemotingException {
+ warning(request);
+ initClient();
+ return client.request(request, timeout);
+ }
+
+ /**
+ * 如果配置了调用warning,则每调用5000次warning一次.
+ * @param request
+ */
+ private void warning(Object request){
+ if (requestWithWarning ){
+ if (warningcount.get() % 5000 == 0){
+ logger.warn(new IllegalStateException("safe guard client , should not be called ,must have a bug."));
+ }
+ warningcount.incrementAndGet() ;
+ }
+ }
+
+ public ChannelHandler getChannelHandler() {
+ checkClient();
+ return client.getChannelHandler();
+ }
+
+ public boolean isConnected() {
+ if (client == null) {
+ return initialState;
+ } else {
+ return client.isConnected();
+ }
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ if (client == null){
+ return InetSocketAddress.createUnresolved(NetUtils.getLocalHost(), 0);
+ } else {
+ return client.getLocalAddress();
+ }
+ }
+
+ public ExchangeHandler getExchangeHandler() {
+ return requestHandler;
+ }
+
+ public void send(Object message) throws RemotingException {
+ initClient();
+ client.send(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ initClient();
+ client.send(message, sent);
+ }
+
+ public boolean isClosed() {
+ if (client != null)
+ return client.isClosed();
+ else
+ return true;
+ }
+
+ public void close() {
+ if (client != null)
+ client.close();
+ }
+
+ public void close(int timeout) {
+ if (client != null)
+ client.close(timeout);
+ }
+
+ public void reset(URL url) {
+ checkClient();
+ client.reset(url);
+ }
+
+ @Deprecated
+ public void reset(Parameters parameters){
+ reset(getUrl().addParameters(parameters.getParameters()));
+ }
+
+ public void reconnect() throws RemotingException {
+ checkClient();
+ client.reconnect();
+ }
+
+ public Object getAttribute(String key) {
+ if (client == null){
+ return null;
+ } else {
+ return client.getAttribute(key);
+ }
+ }
+
+ public void setAttribute(String key, Object value) {
+ checkClient();
+ client.setAttribute(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ checkClient();
+ client.removeAttribute(key);
+ }
+
+ public boolean hasAttribute(String key) {
+ if (client == null){
+ return false;
+ } else {
+ return client.hasAttribute(key);
+ }
+ }
+
+ private void checkClient() {
+ if (client == null) {
+ throw new IllegalStateException(
+ "LazyConnectExchangeClient state error. the client has not be init .url:" + url);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
new file mode 100644
index 0000000..ccfb35c
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Parameters;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+
+/**
+ * dubbo protocol support class.
+ *
+ * @author chao.liuc
+ */
+@SuppressWarnings("deprecation")
+final class ReferenceCountExchangeClient implements ExchangeClient {
+
+ private ExchangeClient client;
+
+ private final URL url;
+
+// private final ExchangeHandler handler;
+
+ private final AtomicInteger refenceCount = new AtomicInteger(0);
+
+ private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap;
+
+
+ public ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap) {
+ this.client = client;
+ refenceCount.incrementAndGet();
+ this.url = client.getUrl();
+ if (ghostClientMap == null){
+ throw new IllegalStateException("ghostClientMap can not be null, url: " + url);
+ }
+ this.ghostClientMap = ghostClientMap;
+ }
+
+ public void reset(URL url) {
+ client.reset(url);
+ }
+
+ public ResponseFuture request(Object request) throws RemotingException {
+ return client.request(request);
+ }
+
+ public URL getUrl() {
+ return client.getUrl();
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return client.getRemoteAddress();
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return client.getChannelHandler();
+ }
+
+ public ResponseFuture request(Object request, int timeout) throws RemotingException {
+ return client.request(request, timeout);
+ }
+
+ public boolean isConnected() {
+ return client.isConnected();
+ }
+
+ public void reconnect() throws RemotingException {
+ client.reconnect();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return client.getLocalAddress();
+ }
+
+ public boolean hasAttribute(String key) {
+ return client.hasAttribute(key);
+ }
+
+ public void reset(Parameters parameters) {
+ client.reset(parameters);
+ }
+
+ public void send(Object message) throws RemotingException {
+ client.send(message);
+ }
+
+ public ExchangeHandler getExchangeHandler() {
+ return client.getExchangeHandler();
+ }
+
+ public Object getAttribute(String key) {
+ return client.getAttribute(key);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ client.send(message, sent);
+ }
+
+ public void setAttribute(String key, Object value) {
+ client.setAttribute(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ client.removeAttribute(key);
+ }
+ /*
+ * close方法将不再幂等,调用需要注意.
+ */
+ public void close() {
+ close(0);
+ }
+
+ public void close(int timeout) {
+ if (refenceCount.decrementAndGet() <= 0){
+ if (timeout == 0){
+ client.close();
+ } else {
+ client.close(timeout);
+ }
+ client = replaceWithLazyClient();
+ }
+ }
+
+ //幽灵client,
+ private LazyConnectExchangeClient replaceWithLazyClient(){
+ //这个操作只为了防止程序bug错误关闭client做的防御措施,初始client必须为false状态
+ URL lazyUrl = url.addParameter(Constants.LAZY_CONNECT_INITIAL_STATE_KEY, Boolean.FALSE)
+ .addParameter(Constants.RECONNECT_KEY, Boolean.FALSE)
+ .addParameter(Constants.SEND_RECONNECT_KEY, Boolean.TRUE.toString())
+ .addParameter("warning", Boolean.TRUE.toString())
+ .addParameter(LazyConnectExchangeClient.REQUEST_WITH_WARNING_KEY, true)
+ .addParameter("_client_memo", "referencecounthandler.replacewithlazyclient");
+
+ String key = url.getAddress();
+ //最差情况下只有一个幽灵连接
+ LazyConnectExchangeClient gclient = ghostClientMap.get(key);
+ if (gclient == null || gclient.isClosed()){
+ gclient = new LazyConnectExchangeClient(lazyUrl, client.getExchangeHandler());
+ ghostClientMap.put(key, gclient);
+ }
+ return gclient;
+ }
+
+ public boolean isClosed() {
+ return client.isClosed();
+ }
+
+ public void incrementAndGetCount(){
+ refenceCount.incrementAndGet();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
new file mode 100644
index 0000000..cc5cf7d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.filter;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.Future;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ResponseCallback;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.StaticContext;
+import com.alibaba.dubbo.rpc.protocol.dubbo.FutureAdapter;
+import com.alibaba.dubbo.rpc.support.RpcUtils;
+
+/**
+ * EventFilter
+ * @author chao.liuc
+ * @author william.liangf
+ */
+@Activate(group = Constants.CONSUMER)
+public class FutureFilter implements Filter {
+
+ protected static final Logger logger = LoggerFactory.getLogger(FutureFilter.class);
+
+ public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
+ final boolean isAsync = RpcUtils.isAsync(invoker.getUrl(), invocation);
+
+ fireInvokeCallback(invoker, invocation);
+ //需要在调用前配置好是否有返回值,已供invoker判断是否需要返回future.
+ Result result = invoker.invoke(invocation);
+ if (isAsync) {
+ asyncCallback(invoker, invocation);
+ } else {
+ syncCallback(invoker, invocation, result);
+ }
+ return result;
+ }
+
+ private void syncCallback(final Invoker<?> invoker, final Invocation invocation, final Result result) {
+ if (result.hasException()) {
+ fireThrowCallback(invoker, invocation, result.getException());
+ } else {
+ fireReturnCallback(invoker, invocation, result.getValue());
+ }
+ }
+
+ private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
+ Future<?> f = RpcContext.getContext().getFuture();
+ if (f instanceof FutureAdapter) {
+ ResponseFuture future = ((FutureAdapter<?>)f).getFuture();
+ future.setCallback(new ResponseCallback() {
+ public void done(Object rpcResult) {
+ if (rpcResult == null){
+ logger.error(new IllegalStateException("invalid result value : null, expected "+Result.class.getName()));
+ return;
+ }
+ ///must be rpcResult
+ if (! (rpcResult instanceof Result)){
+ logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected "+Result.class.getName()));
+ return;
+ }
+ Result result = (Result) rpcResult;
+ if (result.hasException()) {
+ fireThrowCallback(invoker, invocation, result.getException());
+ } else {
+ fireReturnCallback(invoker, invocation, result.getValue());
+ }
+ }
+ public void caught(Throwable exception) {
+ fireThrowCallback(invoker, invocation, exception);
+ }
+ });
+ }
+ }
+
+ private void fireInvokeCallback(final Invoker<?> invoker, final Invocation invocation) {
+ final Method onInvokeMethod = (Method)StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_INVOKE_METHOD_KEY));
+ final Object onInvokeInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_INVOKE_INSTANCE_KEY));
+
+ if (onInvokeMethod == null && onInvokeInst == null ){
+ return ;
+ }
+ if (onInvokeMethod == null || onInvokeInst == null ){
+ throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() +" has a onreturn callback config , but no such "+(onInvokeMethod == null ? "method" : "instance")+" found. url:"+invoker.getUrl());
+ }
+ if (onInvokeMethod != null && ! onInvokeMethod.isAccessible()) {
+ onInvokeMethod.setAccessible(true);
+ }
+
+ Object[] params = invocation.getArguments();
+ try {
+ onInvokeMethod.invoke(onInvokeInst, params);
+ } catch (InvocationTargetException e) {
+ fireThrowCallback(invoker, invocation, e.getTargetException());
+ } catch (Throwable e) {
+ fireThrowCallback(invoker, invocation, e);
+ }
+ }
+
+ private void fireReturnCallback(final Invoker<?> invoker, final Invocation invocation, final Object result) {
+ final Method onReturnMethod = (Method)StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_RETURN_METHOD_KEY));
+ final Object onReturnInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_RETURN_INSTANCE_KEY));
+
+ //not set onreturn callback
+ if (onReturnMethod == null && onReturnInst == null ){
+ return ;
+ }
+
+ if (onReturnMethod == null || onReturnInst == null ){
+ throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() +" has a onreturn callback config , but no such "+(onReturnMethod == null ? "method" : "instance")+" found. url:"+invoker.getUrl());
+ }
+ if (onReturnMethod != null && ! onReturnMethod.isAccessible()) {
+ onReturnMethod.setAccessible(true);
+ }
+
+ Object[] args = invocation.getArguments();
+ Object[] params ;
+ Class<?>[] rParaTypes = onReturnMethod.getParameterTypes() ;
+ if (rParaTypes.length >1 ) {
+ if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)){
+ params = new Object[2];
+ params[0] = result;
+ params[1] = args ;
+ }else {
+ params = new Object[args.length + 1];
+ params[0] = result;
+ System.arraycopy(args, 0, params, 1, args.length);
+ }
+ } else {
+ params = new Object[] { result };
+ }
+ try {
+ onReturnMethod.invoke(onReturnInst, params);
+ } catch (InvocationTargetException e) {
+ fireThrowCallback(invoker, invocation, e.getTargetException());
+ } catch (Throwable e) {
+ fireThrowCallback(invoker, invocation, e);
+ }
+ }
+
+ private void fireThrowCallback(final Invoker<?> invoker, final Invocation invocation, final Throwable exception) {
+ final Method onthrowMethod = (Method)StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_METHOD_KEY));
+ final Object onthrowInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_INSTANCE_KEY));
+
+ //没有设置onthrow callback.
+ if (onthrowMethod == null && onthrowInst == null ){
+ return ;
+ }
+ if (onthrowMethod == null || onthrowInst == null ){
+ throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() +" has a onthrow callback config , but no such "+(onthrowMethod == null ? "method" : "instance")+" found. url:"+invoker.getUrl());
+ }
+ if (onthrowMethod != null && ! onthrowMethod.isAccessible()) {
+ onthrowMethod.setAccessible(true);
+ }
+ Class<?>[] rParaTypes = onthrowMethod.getParameterTypes() ;
+ if (rParaTypes[0].isAssignableFrom(exception.getClass())){
+ try {
+ Object[] args = invocation.getArguments();
+ Object[] params;
+
+ if (rParaTypes.length >1 ) {
+ if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)){
+ params = new Object[2];
+ params[0] = exception;
+ params[1] = args ;
+ }else {
+ params = new Object[args.length + 1];
+ params[0] = exception;
+ System.arraycopy(args, 0, params, 1, args.length);
+ }
+ } else {
+ params = new Object[] { exception };
+ }
+ onthrowMethod.invoke(onthrowInst,params);
+ } catch (Throwable e) {
+ logger.error(invocation.getMethodName() +".call back method invoke error . callback method :" + onthrowMethod + ", url:"+ invoker.getUrl(), e);
+ }
+ } else {
+ logger.error(invocation.getMethodName() +".call back method invoke error . callback method :" + onthrowMethod + ", url:"+ invoker.getUrl(), exception);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java
new file mode 100644
index 0000000..2630923
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.filter;
+
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * TraceFilter
+ *
+ * @author william.liangf
+ */
+@Activate(group = Constants.PROVIDER)
+public class TraceFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(TraceFilter.class);
+
+ private static final String TRACE_MAX = "trace.max";
+
+ private static final String TRACE_COUNT = "trace.count";
+
+ private static final ConcurrentMap<String, Set<Channel>> tracers = new ConcurrentHashMap<String, Set<Channel>>();
+
+ public static void addTracer(Class<?> type, String method, Channel channel, int max) {
+ channel.setAttribute(TRACE_MAX, max);
+ channel.setAttribute(TRACE_COUNT, new AtomicInteger());
+ String key = method != null && method.length() > 0 ? type.getName() + "." + method : type.getName();
+ Set<Channel> channels = tracers.get(key);
+ if (channels == null) {
+ tracers.putIfAbsent(key, new ConcurrentHashSet<Channel>());
+ channels = tracers.get(key);
+ }
+ channels.add(channel);
+ }
+
+ public static void removeTracer(Class<?> type, String method, Channel channel) {
+ channel.removeAttribute(TRACE_MAX);
+ channel.removeAttribute(TRACE_COUNT);
+ String key = method != null && method.length() > 0 ? type.getName() + "." + method : type.getName();
+ Set<Channel> channels = tracers.get(key);
+ if (channels != null) {
+ channels.remove(channel);
+ }
+ }
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ long start = System.currentTimeMillis();
+ Result result = invoker.invoke(invocation);
+ long end = System.currentTimeMillis();
+ if (tracers.size() > 0) {
+ String key = invoker.getInterface().getName() + "." + invocation.getMethodName();
+ Set<Channel> channels = tracers.get(key);
+ if (channels == null || channels.size() == 0) {
+ key = invoker.getInterface().getName();
+ channels = tracers.get(key);
+ }
+ if (channels != null && channels.size() > 0) {
+ for (Channel channel : new ArrayList<Channel>(channels)) {
+ if (channel.isConnected()) {
+ try {
+ int max = 1;
+ Integer m = (Integer) channel.getAttribute(TRACE_MAX);
+ if (m != null) {
+ max = (int) m;
+ }
+ int count = 0;
+ AtomicInteger c = (AtomicInteger) channel.getAttribute(TRACE_COUNT);
+ if (c == null) {
+ c = new AtomicInteger();
+ channel.setAttribute(TRACE_COUNT, c);
+ }
+ count = c.getAndIncrement();
+ if (count < max) {
+ String prompt = channel.getUrl().getParameter(Constants.PROMPT_KEY, Constants.DEFAULT_PROMPT);
+ channel.send("\r\n" + RpcContext.getContext().getRemoteAddress() + " -> "
+ + invoker.getInterface().getName()
+ + "." + invocation.getMethodName()
+ + "(" + JSON.json(invocation.getArguments()) + ")" + " -> " + JSON.json(result.getValue())
+ + "\r\nelapsed: "+(end - start) +" ms."
+ + "\r\n\r\n" + prompt);
+ }
+ if(count >= max - 1) {
+ channels.remove(channel);
+ }
+ } catch (Throwable e) {
+ channels.remove(channel);
+ logger.warn(e.getMessage(), e);
+ }
+ } else {
+ channels.remove(channel);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ClientsPageHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ClientsPageHandler.java
new file mode 100644
index 0000000..dd04d07
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ClientsPageHandler.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.page;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ClientsPageHandler
+ *
+ * @author william.liangf
+ */
+public class ClientsPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String port = url.getParameter("port");
+ int p = port == null || port.length() == 0 ? 0 : Integer.parseInt(port);
+ Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();
+ ExchangeServer server = null;
+ StringBuilder select = new StringBuilder();
+ if (servers != null && servers.size() > 0) {
+ if (servers.size() == 1) {
+ server = servers.iterator().next();
+ String address = server.getUrl().getAddress();
+ select.append(" > " + NetUtils.getHostName(address) + "/" + address);
+ } else {
+ select.append(" > <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() + " > Clients", "Clients (" + rows.size() + ")", new String[]{"Client Address:"}, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ServersPageHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ServersPageHandler.java
new file mode 100644
index 0000000..a02f506
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/page/ServersPageHandler.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.page;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.page.Menu;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ServersPageHandler
+ *
+ * @author william.liangf
+ */
+@Menu(name = "Servers", desc="Show exported service servers.", order = 14000)
+public class ServersPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();
+ int clientCount = 0;
+ if (servers != null && servers.size() > 0) {
+ for (ExchangeServer s : servers) {
+ List<String> row = new ArrayList<String>();
+ String address = s.getUrl().getAddress();
+ row.add(NetUtils.getHostName(address) + "/" + address);
+ int clientSize = s.getExchangeChannels().size();
+ clientCount += clientSize;
+ row.add("<a href=\"clients.html?port=" + s.getUrl().getPort() + "\">Clients(" + clientSize + ")</a>");
+ rows.add(row);
+ }
+ }
+ return new Page("Servers", "Servers (" + rows.size() + ")", new String[]{"Server Address:", "Clients(" + clientCount + ")"}, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java
new file mode 100644
index 0000000..ab8caac
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.status;
+
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ServerStatusChecker
+ *
+ * @author william.liangf
+ */
+@Activate
+public class ServerStatusChecker implements StatusChecker {
+
+ public Status check() {
+ Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();
+ if (servers == null || servers.size() == 0) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Status.Level level = Status.Level.OK;
+ StringBuilder buf = new StringBuilder();
+ for (ExchangeServer server : servers) {
+ if (! server.isBound()) {
+ level = Status.Level.ERROR;
+ buf.setLength(0);
+ buf.append(server.getLocalAddress());
+ break;
+ }
+ if (buf.length() > 0) {
+ buf.append(",");
+ }
+ buf.append(server.getLocalAddress());
+ buf.append("(clients:");
+ buf.append(server.getChannels().size());
+ buf.append(")");
+ }
+ return new Status(level, buf.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java
new file mode 100644
index 0000000..a334d16
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.status;
+
+import java.util.Collection;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer;
+import com.alibaba.dubbo.remoting.transport.dispather.WrappedChannelHandler;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ThreadPoolStatusChecker
+ *
+ * @author william.liangf
+ */
+@Activate
+public class ThreadPoolStatusChecker implements StatusChecker {
+
+ public Status check() {
+ Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();
+ if (servers == null || servers.size() == 0) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ for (Server server : servers) {
+ if (server instanceof HeaderExchangeServer) {
+ HeaderExchangeServer exchanger = (HeaderExchangeServer) server;
+ server = exchanger.getServer();
+ }
+ ChannelHandler handler = server.getChannelHandler();
+ if (handler instanceof WrappedChannelHandler) {
+ Executor executor = ((WrappedChannelHandler) handler).getExecutor();
+ if (executor instanceof ThreadPoolExecutor) {
+ ThreadPoolExecutor tp = (ThreadPoolExecutor)executor;
+ boolean ok = tp.getActiveCount() < tp.getMaximumPoolSize() - 1;
+ return new Status(ok ? Status.Level.OK : Status.Level.WARN,
+ "max:" + tp.getMaximumPoolSize()
+ + ",core:" + tp.getCorePoolSize()
+ + ",largest:" + tp.getLargestPoolSize()
+ + ",active:" + tp.getActiveCount()
+ + ",task:" + tp.getTaskCount());
+ }
+ }
+ }
+ return new Status(Status.Level.UNKNOWN);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandler.java
new file mode 100644
index 0000000..1f998bf
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandler.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ChangeServiceTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "[service]", summary = "Change default service.", detail = "Change default service.")
+public class ChangeTelnetHandler implements TelnetHandler {
+
+ public static final String SERVICE_KEY = "telnet.service";
+
+ public String telnet(Channel channel, String message) {
+ if (message == null || message.length() == 0) {
+ return "Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService";
+ }
+ StringBuilder buf = new StringBuilder();
+ if (message.equals("/") || message.equals("..")) {
+ String service = (String) channel.getAttribute(SERVICE_KEY);
+ channel.removeAttribute(SERVICE_KEY);
+ buf.append("Cancelled default service " + service + ".");
+ } else {
+ boolean found = false;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (message.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || message.equals(exporter.getInvoker().getInterface().getName())
+ || message.equals(exporter.getInvoker().getUrl().getPath())) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ channel.setAttribute(SERVICE_KEY, message);
+ buf.append("Used the " + message + " as default.\r\nYou can cancel default service by command: cd /");
+ } else {
+ buf.append("No such service " + message);
+ }
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CountTelnetHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CountTelnetHandler.java
new file mode 100644
index 0000000..b58be30
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CountTelnetHandler.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.remoting.telnet.support.TelnetUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcStatus;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * CountTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "[service] [method] [times]", summary = "Count the service.", detail = "Count the service.")
+public class CountTelnetHandler implements TelnetHandler {
+
+ public String telnet(final Channel channel, String message) {
+ String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ if ((service == null || service.length() == 0)
+ && (message == null || message.length() == 0)) {
+ return "Please input service name, eg: \r\ncount XxxService\r\ncount XxxService xxxMethod\r\ncount XxxService xxxMethod 10\r\nor \"cd XxxService\" firstly.";
+ }
+ StringBuilder buf = new StringBuilder();
+ if (service != null && service.length() > 0) {
+ buf.append("Use default service " + service + ".\r\n");
+ }
+ String[] parts = message.split("\\s+");
+ String method;
+ String times;
+ if (service == null || service.length() == 0) {
+ service = parts.length > 0 ? parts[0] : null;
+ method = parts.length > 1 ? parts[1] : null;
+ } else {
+ method = parts.length > 0 ? parts[0] : null;
+ }
+ if (StringUtils.isInteger(method)) {
+ times = method;
+ method = null;
+ } else {
+ times = parts.length > 2 ? parts[2] : "1";
+ }
+ if (! StringUtils.isInteger(times)) {
+ return "Illegal times " + times + ", must be integer.";
+ }
+ final int t = Integer.parseInt(times);
+ Invoker<?> invoker = null;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (service.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || service.equals(exporter.getInvoker().getInterface().getName())
+ || service.equals(exporter.getInvoker().getUrl().getPath())) {
+ invoker = exporter.getInvoker();
+ break;
+ }
+ }
+ if (invoker != null) {
+ if (t > 0) {
+ final String mtd = method;
+ final Invoker<?> inv = invoker;
+ final String prompt = channel.getUrl().getParameter("prompt", "telnet");
+ Thread thread = new Thread(new Runnable() {
+ public void run() {
+ for (int i = 0; i < t; i ++) {
+ String result = count(inv, mtd);
+ try {
+ channel.send("\r\n" + result);
+ } catch (RemotingException e1) {
+ return;
+ }
+ if (i < t - 1) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ try {
+ channel.send("\r\n" + prompt + "> ");
+ } catch (RemotingException e1) {
+ return;
+ }
+ }
+ }, "TelnetCount");
+ thread.setDaemon(true);
+ thread.start();
+ }
+ } else {
+ buf.append("No such service " + service);
+ }
+ return buf.toString();
+ }
+
+ private String count(Invoker<?> invoker, String method) {
+ URL url = invoker.getUrl();
+ List<List<String>> table = new ArrayList<List<String>>();
+ List<String> header = new ArrayList<String>();
+ header.add("method");
+ header.add("total");
+ header.add("failed");
+ header.add("active");
+ header.add("average");
+ header.add("max");
+ if (method == null || method.length() == 0) {
+ for (Method m : invoker.getInterface().getMethods()) {
+ RpcStatus count = RpcStatus.getStatus(url, m.getName());
+ List<String> row = new ArrayList<String>();
+ row.add(m.getName());
+ row.add(String.valueOf(count.getTotal()));
+ row.add(String.valueOf(count.getFailed()));
+ row.add(String.valueOf(count.getActive()));
+ row.add(String.valueOf(count.getSucceededAverageElapsed()) + "ms");
+ row.add(String.valueOf(count.getSucceededMaxElapsed()) + "ms");
+ table.add(row);
+ }
+ } else {
+ boolean found = false;
+ for (Method m : invoker.getInterface().getMethods()) {
+ if (m.getName().equals(method)) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ RpcStatus count = RpcStatus.getStatus(url, method);
+ List<String> row = new ArrayList<String>();
+ row.add(method);
+ row.add(String.valueOf(count.getTotal()));
+ row.add(String.valueOf(count.getFailed()));
+ row.add(String.valueOf(count.getActive()));
+ row.add(String.valueOf(count.getSucceededAverageElapsed()) + "ms");
+ row.add(String.valueOf(count.getSucceededMaxElapsed()) + "ms");
+ table.add(row);
+ } else {
+ return "No such method " + method + " in class " + invoker.getInterface().getName();
+ }
+ }
+ return TelnetUtils.toTable(header, table);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandler.java
new file mode 100644
index 0000000..529c05b
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandler.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+
+/**
+ * CurrentServiceTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "", summary = "Print working default service.", detail = "Print working default service.")
+public class CurrentTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ if (message.length() > 0) {
+ return "Unsupported parameter " + message + " for pwd.";
+ }
+ String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ StringBuilder buf = new StringBuilder();
+ if (service == null || service.length() == 0) {
+ buf.append("/");
+ } else {
+ buf.append(service);
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java
new file mode 100644
index 0000000..4bd1689
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.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.telnet;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * InvokeTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "[service.]method(args)", summary = "Invoke the service method.", detail = "Invoke the service method.")
+public class InvokeTelnetHandler implements TelnetHandler {
+
+ @SuppressWarnings("unchecked")
+ public String telnet(Channel channel, String message) {
+ if (message == null || message.length() == 0) {
+ return "Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})";
+ }
+ StringBuilder buf = new StringBuilder();
+ String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ if (service != null && service.length() > 0) {
+ buf.append("Use default service " + service + ".\r\n");
+ }
+ int i = message.indexOf("(");
+ if (i < 0 || ! message.endsWith(")")) {
+ return "Invalid parameters, format: service.method(args)";
+ }
+ String method = message.substring(0, i).trim();
+ String args = message.substring(i + 1, message.length() - 1).trim();
+ i = method.lastIndexOf(".");
+ if (i >= 0) {
+ service = method.substring(0, i).trim();
+ method = method.substring(i + 1).trim();
+ }
+ List<Object> list;
+ try {
+ list = (List<Object>) JSON.parse("[" + args + "]", List.class);
+ } catch (Throwable t) {
+ return "Invalid json argument, cause: " + t.getMessage();
+ }
+ Invoker<?> invoker = null;
+ Method invokeMethod = null;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (service == null || service.length() == 0) {
+ invokeMethod = findMethod(exporter, method, list);
+ if (invokeMethod != null) {
+ invoker = exporter.getInvoker();
+ break;
+ }
+ } else {
+ if (service.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || service.equals(exporter.getInvoker().getInterface().getName())
+ || service.equals(exporter.getInvoker().getUrl().getPath())) {
+ invokeMethod = findMethod(exporter, method, list);
+ invoker = exporter.getInvoker();
+ break;
+ }
+ }
+ }
+ if (invoker != null) {
+ if (invokeMethod != null) {
+ 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();
+ }
+
+ private static Method findMethod(Exporter<?> exporter, String method, List<Object> args) {
+ Invoker<?> invoker = exporter.getInvoker();
+ Method[] methods = invoker.getInterface().getMethods();
+ Method invokeMethod = null;
+ for (Method m : methods) {
+ if (m.getName().equals(method) && m.getParameterTypes().length == args.size()) {
+ if (invokeMethod != null) { // 重载
+ if (isMatch(invokeMethod.getParameterTypes(), args)) {
+ invokeMethod = m;
+ break;
+ }
+ } else {
+ invokeMethod = m;
+ }
+ invoker = exporter.getInvoker();
+ }
+ }
+ return invokeMethod;
+ }
+
+ private static boolean isMatch(Class<?>[] types, List<Object> args) {
+ if (types.length != args.size()) {
+ return false;
+ }
+ for (int i = 0; i < types.length; i ++) {
+ Class<?> type = types[i];
+ Object arg = args.get(i);
+ if (ReflectUtils.isPrimitive(arg.getClass())) {
+ if (! ReflectUtils.isPrimitive(type)) {
+ return false;
+ }
+ } else if (arg instanceof Map) {
+ String name = (String) ((Map<?, ?>)arg).get("class");
+ Class<?> cls = arg.getClass();
+ if (name != null && name.length() > 0) {
+ cls = ReflectUtils.forName(name);
+ }
+ if (! type.isAssignableFrom(cls)) {
+ return false;
+ }
+ } else if (arg instanceof Collection) {
+ if (! type.isArray() && ! type.isAssignableFrom(arg.getClass())) {
+ return false;
+ }
+ } else {
+ if (! type.isAssignableFrom(arg.getClass())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandler.java
new file mode 100644
index 0000000..2f60210
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandler.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ListTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "[-l] [service]", summary = "List services and methods.", detail = "List services and methods.")
+public class ListTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ StringBuilder buf = new StringBuilder();
+ String service = null;
+ boolean detail = false;
+ if (message.length() > 0) {
+ String[] parts = message.split("\\s+");
+ for (String part : parts) {
+ if ("-l".equals(part)) {
+ detail = true;
+ } else {
+ if (service != null && service.length() > 0) {
+ return "Invaild parameter " + part;
+ }
+ service = part;
+ }
+ }
+ } else {
+ service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ if (service != null && service.length() > 0) {
+ buf.append("Use default service " + service + ".\r\n");
+ }
+ }
+ if (service == null || service.length() == 0) {
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ buf.append(exporter.getInvoker().getInterface().getName());
+ if (detail) {
+ buf.append(" -> ");
+ buf.append(exporter.getInvoker().getUrl());
+ }
+ }
+ } else {
+ Invoker<?> invoker = null;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (service.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || service.equals(exporter.getInvoker().getInterface().getName())
+ || service.equals(exporter.getInvoker().getUrl().getPath())) {
+ invoker = exporter.getInvoker();
+ break;
+ }
+ }
+ if (invoker != null) {
+ Method[] methods = invoker.getInterface().getMethods();
+ for (Method method : methods) {
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ if (detail) {
+ buf.append(ReflectUtils.getName(method));
+ } else {
+ buf.append(method.getName());
+ }
+ }
+ } else {
+ buf.append("No such service " + service);
+ }
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandler.java
new file mode 100644
index 0000000..e2c37df
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.logger.Level;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+
+/**
+ * LogTelnetHandler
+ * @author chao.liuc
+ *
+ */
+@Activate
+@Help(parameter = "level", summary = "Change log level or show log ", detail = "Change log level or show log")
+public class LogTelnetHandler implements TelnetHandler {
+
+ public static final String SERVICE_KEY = "telnet.log";
+
+ public String telnet(Channel channel, String message) {
+ long size = 0 ;
+ File file = LoggerFactory.getFile();
+ StringBuffer buf = new StringBuffer();
+ if (message == null || message.trim().length() == 0) {
+ buf.append("EXAMPLE: log error / log 100");
+ }else {
+ String str[] = message.split(" ");
+ if (! StringUtils.isInteger(str[0])){
+ LoggerFactory.setLevel(Level.valueOf(message.toUpperCase()));
+ } else {
+ int SHOW_LOG_LENGTH = Integer.parseInt(str[0]);
+
+ if (file != null && file.exists()) {
+ try{
+ FileInputStream fis = new FileInputStream(file);
+ FileChannel filechannel = fis.getChannel();
+ size = filechannel.size();
+ ByteBuffer bb;
+ if (size <= SHOW_LOG_LENGTH) {
+ bb = ByteBuffer.allocate((int) size);
+ filechannel.read(bb, 0);
+ } else {
+ int pos = (int) (size - SHOW_LOG_LENGTH);
+ bb = ByteBuffer.allocate(SHOW_LOG_LENGTH);
+ filechannel.read(bb, pos);
+ }
+ bb.flip();
+ String content = new String(bb.array()).replace("<", "<")
+ .replace(">", ">").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/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandler.java
new file mode 100644
index 0000000..62add42
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ServerTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "[-l] [port]", summary = "Print server ports and connections.", detail = "Print server ports and connections.")
+public class PortTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ StringBuilder buf = new StringBuilder();
+ String port = null;
+ boolean detail = false;
+ if (message.length() > 0) {
+ String[] parts = message.split("\\s+");
+ for (String part : parts) {
+ if ("-l".equals(part)) {
+ detail = true;
+ } else {
+ if (! StringUtils.isInteger(part)) {
+ return "Illegal port " + part + ", must be integer.";
+ }
+ port = part;
+ }
+ }
+ }
+ if (port == null || port.length() == 0) {
+ for (ExchangeServer server : DubboProtocol.getDubboProtocol().getServers()) {
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ if (detail) {
+ buf.append(server.getUrl().getProtocol() + "://" + server.getUrl().getAddress());
+ } else {
+ buf.append(server.getUrl().getPort());
+ }
+ }
+ } else {
+ int p = Integer.parseInt(port);
+ ExchangeServer server = null;
+ for (ExchangeServer s : DubboProtocol.getDubboProtocol().getServers()) {
+ if (p == s.getUrl().getPort()) {
+ server = s;
+ break;
+ }
+ }
+ if (server != null) {
+ Collection<ExchangeChannel> channels = server.getExchangeChannels();
+ for (ExchangeChannel c : channels) {
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ if (detail) {
+ buf.append(c.getRemoteAddress() + " -> " + c.getLocalAddress());
+ } else {
+ buf.append(c.getRemoteAddress());
+ }
+ }
+ } else {
+ buf.append("No such port " + port);
+ }
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.java
new file mode 100644
index 0000000..615438d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+import com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter;
+
+/**
+ * TraceTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Activate
+@Help(parameter = "[service] [method] [times]", summary = "Trace the service.", detail = "Trace the service.")
+public class TraceTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ if ((service == null || service.length() == 0)
+ && (message == null || message.length() == 0)) {
+ return "Please input service name, eg: \r\ntrace XxxService\r\ntrace XxxService xxxMethod\r\ntrace XxxService xxxMethod 10\r\nor \"cd XxxService\" firstly.";
+ }
+ String[] parts = message.split("\\s+");
+ String method;
+ String times;
+ if (service == null || service.length() == 0) {
+ service = parts.length > 0 ? parts[0] : null;
+ method = parts.length > 1 ? parts[1] : null;
+ } else {
+ method = parts.length > 0 ? parts[0] : null;
+ }
+ if (StringUtils.isInteger(method)) {
+ times = method;
+ method = null;
+ } else {
+ times = parts.length > 2 ? parts[2] : "1";
+ }
+ if (! StringUtils.isInteger(times)) {
+ return "Illegal times " + times + ", must be integer.";
+ }
+ Invoker<?> invoker = null;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (service.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || service.equals(exporter.getInvoker().getInterface().getName())
+ || service.equals(exporter.getInvoker().getUrl().getPath())) {
+ invoker = exporter.getInvoker();
+ break;
+ }
+ }
+ if (invoker != null) {
+ if (method != null && method.length() > 0) {
+ boolean found = false;
+ for (Method m : invoker.getInterface().getMethods()) {
+ if (m.getName().equals(method)) {
+ found = true;
+ break;
+ }
+ }
+ if (! found) {
+ return "No such method " + method + " in class " + invoker.getInterface().getName();
+ }
+ }
+ TraceFilter.addTracer(invoker.getInterface(), method, channel, Integer.parseInt(times));
+ } else {
+ return "No such service " + service;
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..2729db5
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+server=com.alibaba.dubbo.rpc.protocol.dubbo.status.ServerStatusChecker
+threadpool=com.alibaba.dubbo.rpc.protocol.dubbo.status.ThreadPoolStatusChecker
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..63122b4
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,2 @@
+servers=com.alibaba.dubbo.rpc.protocol.dubbo.page.ServersPageHandler
+clients=com.alibaba.dubbo.rpc.protocol.dubbo.page.ClientsPageHandler
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec
new file mode 100644
index 0000000..3c6a1b4
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.Codec
@@ -0,0 +1 @@
+dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler
new file mode 100644
index 0000000..780deef
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler
@@ -0,0 +1,7 @@
+ls=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ListTelnetHandler
+ps=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.PortTelnetHandler
+cd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler
+pwd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler
+invoke=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler
+trace=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler
+count=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..ff24401
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1,2 @@
+trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter
+future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..6725d68
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
new file mode 100644
index 0000000..4c21d1e
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+/**
+ * 测试 dubboInvoker的Avilable状态
+ * @author chao.liuc
+ *
+ */
+public class DubboInvokerAvilableTest {
+ private static DubboProtocol protocol = DubboProtocol.getDubboProtocol();
+ private static ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @Test
+ public void test_Normal_available(){
+ URL url = URL.valueOf("dubbo://127.0.0.1:20883/hi");
+ ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+
+ DubboInvoker<?> invoker = (DubboInvoker<?>)protocol.refer(IDemoService.class, url);
+ Assert.assertEquals(true, invoker.isAvailable());
+ invoker.destroy();
+ Assert.assertEquals(false, invoker.isAvailable());
+ }
+
+ @Test
+ public void test_Normal_ChannelReadOnly() throws Exception{
+ URL url = URL.valueOf("dubbo://127.0.0.1:20883/hi");
+ ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+
+ DubboInvoker<?> invoker = (DubboInvoker<?>)protocol.refer(IDemoService.class, url);
+ Assert.assertEquals(true, invoker.isAvailable());
+
+ getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
+
+ Assert.assertEquals(false, invoker.isAvailable());
+
+ //恢复状态,invoker共享连接
+ getClients(invoker)[0].removeAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY);
+ }
+
+ @Test
+ public void test_NoInvokers() throws Exception{
+ URL url = URL.valueOf("dubbo://127.0.0.1:20883/hi?connections=1");
+ ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+
+ DubboInvoker<?> invoker = (DubboInvoker<?>)protocol.refer(IDemoService.class, url);
+
+ ExchangeClient[] clients = getClients(invoker);
+ clients[0].close();
+ Assert.assertEquals(false, invoker.isAvailable());
+
+ }
+
+ @Test
+ public void test_Lazy_ChannelReadOnly() throws Exception{
+ URL url = URL.valueOf("dubbo://127.0.0.1:20883/hi?lazy=true&connections=1");
+ ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+
+ DubboInvoker<?> invoker = (DubboInvoker<?>)protocol.refer(IDemoService.class, url);
+ Assert.assertEquals(true, invoker.isAvailable());
+
+ try{
+ getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
+ fail();
+ }catch (IllegalStateException e) {
+
+ }
+ //invoke method --> init client
+
+ IDemoService service = (IDemoService)proxy.getProxy(invoker);
+ Assert.assertEquals("ok", service.get());
+
+ Assert.assertEquals(true, invoker.isAvailable());
+ getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
+ Assert.assertEquals(false, invoker.isAvailable());
+ }
+
+ private ExchangeClient[] getClients(DubboInvoker<?> invoker) throws Exception{
+ Field field = DubboInvoker.class.getDeclaredField("clients");
+ field.setAccessible(true);
+ ExchangeClient[] clients = (ExchangeClient[])field.get(invoker);
+ Assert.assertEquals(1, clients.length);
+ return clients;
+ }
+
+ public interface IDemoService{
+ public String get();
+ }
+ public class DemoServiceImpl implements IDemoService{
+ public String get(){
+ return "ok";
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java
new file mode 100644
index 0000000..723d6ed
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+/**
+ * dubbo protocol lazy connect test
+ * @author chao.liuc
+ *
+ */
+public class DubboLazyConnectTest {
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @Test(expected = RpcException.class)
+ public void testSticky1(){
+ URL url = URL.valueOf("dubbo://127.0.0.1:9090/hi");
+ ProtocolUtils.refer(IDemoService.class, url);
+ }
+
+ @Test
+ public void testSticky2(){
+ URL url = URL.valueOf("dubbo://127.0.0.1:9090/hi?"+Constants.LAZY_CONNECT_KEY+"=true");
+ ProtocolUtils.refer(IDemoService.class, url);
+ }
+
+ @Test(expected = RpcException.class)
+ public void testSticky3() {
+ URL url = URL.valueOf("dubbo://127.0.0.1:9090/hi?"+Constants.LAZY_CONNECT_KEY+"=true");
+ IDemoService service = (IDemoService)ProtocolUtils.refer(IDemoService.class, url);
+ service.get();
+ }
+
+ @Test
+ public void testSticky4() {
+ int port = NetUtils.getAvailablePort();
+ URL url = URL.valueOf("dubbo://127.0.0.1:"+port+"/hi?"+Constants.LAZY_CONNECT_KEY+"=true");
+
+ ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+
+ IDemoService service = (IDemoService)ProtocolUtils.refer(IDemoService.class, url);
+ Assert.assertEquals("ok", service.get());
+ }
+
+ public interface IDemoService{
+ public String get();
+ }
+ public class DemoServiceImpl implements IDemoService{
+ public String get(){
+ return "ok";
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
new file mode 100644
index 0000000..118517a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+
+import static junit.framework.Assert.assertEquals;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.NonSerialized;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.RemoteService;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.RemoteServiceImpl;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.Type;
+import com.alibaba.dubbo.rpc.service.EchoService;
+
+/**
+ * <code>ProxiesTest</code>
+ */
+
+public class DubboProtocolTest
+{
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ @Test
+ public void testDemoProtocol() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange")));
+ assertEquals(service.getSize(new String[]{"", "", ""}), 3);
+ }
+
+ @Test
+ public void testDubboProtocol() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName())));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName())));
+ assertEquals(service.enumlength(new Type[]{}), Type.Lower);
+ assertEquals(service.getSize(null), -1);
+ assertEquals(service.getSize(new String[]{"", "", ""}), 3);
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("aa", "bb");
+ Set<String> set = service.keys(map);
+ assertEquals(set.size(), 1);
+ assertEquals(set.iterator().next(), "aa");
+ service.invoke("dubbo://127.0.0.1:9010/" + DemoService.class.getName() + "", "invoke");
+
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName() + "?client=netty")));
+ // test netty client
+ StringBuffer buf = new StringBuffer();
+ for(int i=0;i<1024*32+32;i++)
+ buf.append('A');
+ System.out.println(service.stringLength(buf.toString()));
+
+ // cast to EchoService
+ EchoService echo = proxy.getProxy(protocol.refer(EchoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName() + "?client=netty")));
+ assertEquals(echo.$echo(buf.toString()), buf.toString());
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+ }
+
+ @Test
+ public void testDubboProtocolMultiService() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName())));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + DemoService.class.getName())));
+
+ RemoteService remote = new RemoteServiceImpl();
+ protocol.export(proxy.getInvoker(remote, RemoteService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + RemoteService.class.getName())));
+ remote = proxy.getProxy(protocol.refer(RemoteService.class, URL.valueOf("dubbo://127.0.0.1:9010/" + RemoteService.class.getName())));
+
+ service.sayHello("world");
+
+ // test netty client
+ assertEquals("world", service.echo("world"));
+ assertEquals("hello world@" + RemoteServiceImpl.class.getName(), remote.sayHello("world"));
+
+ EchoService serviceEcho = (EchoService)service;
+ assertEquals(serviceEcho.$echo("test"), "test");
+
+ EchoService remoteEecho = (EchoService)remote;
+ assertEquals(remoteEecho.$echo("ok"), "ok");
+ }
+
+ @Test
+ public void testPerm() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));
+ long start = System.currentTimeMillis();
+ for(int i=0;i<1000;i++)
+ service.getSize(new String[]{"", "", ""});
+ System.out.println("take:"+(System.currentTimeMillis()-start));
+ }
+
+ @Test
+ public void testNonSerializedParameter() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));
+ try {
+ service.nonSerializedParameter(new NonSerialized());
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("com.alibaba.dubbo.rpc.protocol.dubbo.support.NonSerialized must implement java.io.Serializable"));
+ }
+ }
+
+ @Test
+ public void testReturnNonSerialized() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/" + DemoService.class.getName() + "?codec=exchange")));
+ try {
+ service.returnNonSerialized();
+ Assert.fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("com.alibaba.dubbo.rpc.protocol.dubbo.support.NonSerialized must implement java.io.Serializable"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java
new file mode 100644
index 0000000..bc1a28b
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+public class ExplicitCallbackTest {
+
+ protected Exporter<IDemoService> exporter = null;
+ protected Exporter<IHelloService> hello_exporter = null;
+ protected Invoker<IDemoService> reference = null;
+
+ @After
+ public void tearDown(){
+ destroyService();
+ }
+
+ public void exportService(){
+ //先export一个service,测试共享连接的问题
+ serviceURL=serviceURL.addParameter("connections", 1);
+ URL hellourl = serviceURL.setPath(IHelloService.class.getName());
+ hello_exporter = ProtocolUtils.export(new HelloServiceImpl(), IHelloService.class, hellourl);
+ exporter = ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, serviceURL);
+ }
+ void referService() {
+ demoProxy = (IDemoService)ProtocolUtils.refer(IDemoService.class, consumerUrl);
+ }
+
+ protected URL serviceURL = null ;
+ protected URL consumerUrl = null ;
+
+ @Before
+ public void setUp(){
+ }
+ public void initOrResetUrl(int callbacks, int timeout) throws Exception {
+ int port = NetUtils.getAvailablePort() ;
+ consumerUrl = serviceURL = URL.valueOf("dubbo://127.0.0.1:"+port+"/"+IDemoService.class.getName()+"?group=test"
+ +"&xxx.0.callback=true"
+ +"&xxx2.0.callback=true"
+ +"&unxxx2.0.callback=false"
+ +"&timeout="+timeout
+ +"&retries=0"
+ +"&"+Constants.CALLBACK_INSTANCES_LIMIT_KEY+"="+callbacks
+ );
+ // uncomment is unblock invoking
+// serviceURL = serviceURL.addParameter("yyy."+Constants.ASYNC_KEY,String.valueOf(true));
+// consumerUrl = consumerUrl.addParameter("yyy."+Constants.ASYNC_KEY,String.valueOf(true));
+ }
+ public void initOrResetBadUrl() throws Exception{
+ initOrResetUrl(1, 1000);
+ consumerUrl = serviceURL = serviceURL
+ .addParameter(Constants.DOWNSTREAM_CODEC_KEY, "dubbo1compatible")
+ ;
+ }
+ public void initOrResetService(){
+ destroyService();
+ exportService();
+ referService();
+ }
+ public void destroyService(){
+ demoProxy = null ;
+ try {
+ if (exporter!=null) exporter.unexport();
+ if (hello_exporter!=null) hello_exporter.unexport();
+ if (reference!=null) reference.destroy();
+ }catch (Exception e) {
+ }
+ }
+ // ============================华丽的分割线================================================
+ interface IDemoCallback{
+ String yyy(String msg);
+ }
+ interface IHelloService{
+ public String sayHello();
+ }
+
+ interface IDemoService{
+ public String get();
+ public int getCallbackCount();
+ public void xxx(IDemoCallback callback,String arg1,int runs,int sleep);
+ public void xxx2(IDemoCallback callback);
+ public void unxxx2(IDemoCallback callback);
+ }
+
+ class HelloServiceImpl implements IHelloService{
+ public String sayHello() {
+ return "hello";
+ }
+
+ }
+ class DemoServiceImpl implements IDemoService {
+ public String get(){
+ return "ok" ;
+ }
+ public void xxx(final IDemoCallback callback ,String arg1, final int runs ,final int sleep) {
+ callback.yyy("Sync callback msg .This is callback data. arg1:"+arg1);
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ for(int i = 0 ;i< runs ;i++){
+ String ret = callback.yyy("server invoke callback : arg:"+System.currentTimeMillis());
+ System.out.println("callback result is :"+ret);
+ try {
+ Thread.sleep(sleep);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ });
+ t.setDaemon(true);
+ t.start();
+ System.out.println("xxx invoke complete");
+ }
+
+ private List<IDemoCallback> callbacks = new ArrayList<IDemoCallback>();
+ public int getCallbackCount(){
+ return callbacks.size();
+ }
+ public void xxx2(IDemoCallback callback){
+ if (!callbacks.contains(callback)){
+ callbacks.add(callback);
+ }
+ startThread();
+ }
+ private volatile Thread t = null;
+ private volatile Lock lock = new ReentrantLock();
+ private void startThread(){
+ if (t == null || callbacks.size() == 0){
+ try{
+ lock.lock();
+ t = new Thread(new Runnable() {
+ public void run() {
+ while(callbacks.size()>0){
+ try {
+ List<IDemoCallback> callbacksCopy = new ArrayList<IDemoCallback>(callbacks);
+ for(IDemoCallback callback : callbacksCopy){
+ try{
+ callback.yyy("this is callback msg,current time is :"+ System.currentTimeMillis());
+ }catch (Exception e) {
+ e.printStackTrace();
+ callbacks.remove(callback);
+ }
+ }
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ });
+ t.setDaemon(true);
+ t.start();
+ }finally{
+ lock.unlock();
+ }
+ }
+ }
+
+ public void unxxx2(IDemoCallback callback) {
+ if (!callbacks.contains(callback)){
+ throw new IllegalStateException("callback instance not found");
+ }
+ callbacks.remove(callback);
+ }
+ }
+
+
+ // ============================华丽的分割线================================================
+ IDemoService demoProxy = null;
+ @Test
+ public void TestCallbackNormal() throws Exception {
+
+ initOrResetUrl(1, 10000000); initOrResetService() ;
+ final AtomicInteger count = new AtomicInteger(0);
+
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+ System.out.println("Async...");
+// Thread.sleep(10000000);
+ assertCallbackCount(10,100,count);
+ destroyService();
+
+
+ }
+
+ @Test
+ public void TestCallbackMultiInstans() throws Exception {
+ initOrResetUrl(2, 1000);
+ initOrResetService() ;
+ IDemoCallback callback = new IDemoCallback(){
+ public String yyy(String msg) {
+ System.out.println("callback1:"+msg);
+ return "callback1 onChanged ,"+msg;
+ }
+ };
+
+ IDemoCallback callback2 = new IDemoCallback(){
+ public String yyy(String msg) {
+ System.out.println("callback2:"+msg);
+ return "callback2 onChanged ,"+msg;
+ }
+ };
+ {
+ demoProxy.xxx2(callback);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+ Thread.sleep(500);
+ demoProxy.unxxx2(callback);
+ Assert.assertEquals(0, demoProxy.getCallbackCount());
+ System.out.println("");
+
+ demoProxy.xxx2(callback2);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+ Thread.sleep(500);
+ demoProxy.unxxx2(callback2);
+ Assert.assertEquals(0, demoProxy.getCallbackCount());
+ System.out.println("");
+ demoProxy.xxx2(callback);
+ Thread.sleep(500);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+ demoProxy.unxxx2(callback);
+ Assert.assertEquals(0, demoProxy.getCallbackCount());
+ }
+ {
+ demoProxy.xxx2(callback);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+
+ demoProxy.xxx2(callback);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+
+ demoProxy.xxx2(callback2);
+ Assert.assertEquals(2, demoProxy.getCallbackCount());
+ }
+ destroyService();
+ }
+
+// @Ignore
+ @Test(expected=RpcException.class)
+ public void TestCallbackDownStreamCodec() throws Exception {
+ initOrResetBadUrl(); initOrResetService() ;
+ final AtomicInteger count = new AtomicInteger(0);
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+ System.out.println("Async...");
+ assertCallbackCount(10,100,count);
+ destroyService();
+ }
+
+ @Test(expected = RpcException.class)
+ public void TestCallbackConsumerLimit() throws Exception {
+ initOrResetUrl(1, 1000);
+ //api的方式 url 无法自动从服务端传递到客户端,需要手动制定
+ initOrResetService() ;
+ final AtomicInteger count = new AtomicInteger(0);
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+ destroyService();
+ }
+
+ @Test(expected = RpcException.class)
+ public void TestCallbackProviderLimit() throws Exception {
+ initOrResetUrl(1, 1000);
+ //api的方式 url 无法自动从服务端传递到客户端,需要手动制定
+ serviceURL = serviceURL.addParameter(Constants.CALLBACK_INSTANCES_LIMIT_KEY, 1+"");
+ initOrResetService() ;
+ final AtomicInteger count = new AtomicInteger(0);
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+ destroyService();
+ }
+
+ private void assertCallbackCount(int runs, int sleep, AtomicInteger count) throws InterruptedException{
+ int last = count.get();
+ for(int i=0;i< runs ;i++){
+ if (last > runs) break;
+ Thread.sleep(sleep * 2);
+ System.out.println(count.get() + " " + last);
+ Assert.assertTrue(count.get() > last);
+ last = count.get();
+ }
+ //有一次同步调用callback
+ Assert.assertEquals(runs+1, count.get());
+ }
+
+ @Ignore //使用不同进程启动
+ @Test
+ public void startProvider() throws Exception {
+ exportService();
+ synchronized (ExplicitCallbackTest.class) {
+ ExplicitCallbackTest.class.wait();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureFilterTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
new file mode 100644
index 0000000..15d06a0
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureFilterTest.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 static org.junit.Assert.assertEquals;
+
+import org.easymock.EasyMock;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.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.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 RpcInvocation invocation;
+
+ @BeforeClass
+ public static void setUp() {
+ invocation = new RpcInvocation();
+ invocation.setMethodName("echo");
+ invocation.setParameterTypes(new Class<?>[] { Enum.class });
+ invocation.setArguments(new Object[] { "hello" });
+ }
+
+ @Test
+ public void testSyncCallback() {
+ @SuppressWarnings("unchecked")
+ Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setValue("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = eventFilter.invoke(invoker, invocation);
+ assertEquals("High", filterResult.getValue());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testSyncCallbackHasException() throws RpcException, Throwable {
+ @SuppressWarnings("unchecked")
+ Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setException(new RuntimeException());
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&"+Constants.ON_THROW_METHOD_KEY+"=echo");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ eventFilter.invoke(invoker, invocation).recreate();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java
new file mode 100644
index 0000000..936e582
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.StaticContext;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+public class ImplicitCallBackTest{
+
+ protected Exporter<IDemoService> exporter = null;
+ protected Invoker<IDemoService> reference = null;
+
+ protected URL serviceURL = null ;
+ protected URL consumerUrl = null ;
+ Method onReturnMethod;
+ Method onThrowMethod ;
+ Method onInvokeMethod ;
+
+ @Before
+ public void setUp() throws SecurityException, NoSuchMethodException{
+ onReturnMethod = Nofify.class.getMethod("onreturn", new Class<?>[]{Person.class, Integer.class});
+ onThrowMethod = Nofify.class.getMethod("onthrow", new Class<?>[]{Throwable.class, Integer.class});
+ onInvokeMethod = Nofify.class.getMethod("oninvoke", new Class<?>[]{Integer.class});
+ }
+
+ @After
+ public void tearDown(){
+ ProtocolUtils.closeAll();
+ }
+
+ public void initOrResetService(){
+ destroyService();
+ exportService();
+ referService();
+ }
+ public void destroyService(){
+ demoProxy = null ;
+ try {
+ if (exporter!=null) exporter.unexport();
+ if (reference!=null) reference.destroy();
+ }catch (Exception e) {
+ }
+ }
+
+ void referService() {
+ demoProxy = (IDemoService)ProtocolUtils.refer(IDemoService.class, consumerUrl);
+ }
+
+ public void exportService(){
+ exporter = ProtocolUtils.export(new NormalDemoService(), IDemoService.class, serviceURL);
+ }
+ public void exportExService(){
+ exporter = ProtocolUtils.export(new ExceptionDemoExService(), IDemoService.class, serviceURL);
+ }
+
+ public void initOrResetUrl(boolean isAsync) throws Exception {
+ int port = NetUtils.getAvailablePort() ;
+ consumerUrl = serviceURL = URL.valueOf("dubbo://127.0.0.1:"+port+"/"+IDemoService.class.getName()+"?group=test&async="+isAsync+"&timeout=100000&reference.filter=future" );
+ StaticContext.getSystemContext().clear();
+ }
+
+ public void initImplicitCallBackURL_onlyOnthrow() throws Exception {
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_THROW_METHOD_KEY),onThrowMethod);
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_THROW_INSTANCE_KEY),notify);
+ }
+ public void initImplicitCallBackURL_onlyOnreturn() throws Exception {
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_RETURN_METHOD_KEY),onReturnMethod);
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_RETURN_INSTANCE_KEY),notify);
+
+ }
+ public void initImplicitCallBackURL_onlyOninvoke() throws Exception {
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_INVOKE_METHOD_KEY),onInvokeMethod);
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", Constants.ON_INVOKE_INSTANCE_KEY),notify);
+ }
+
+ //================================================================================================
+
+ NofifyImpl notify = new NofifyImpl();
+
+ interface Nofify {
+ public void onreturn(Person msg, Integer id);
+ public void onthrow(Throwable ex, Integer id);
+ public void oninvoke(Integer id);
+ }
+ class NofifyImpl implements Nofify{
+ public List<Integer> inv = new ArrayList<Integer> ();
+ public Map<Integer ,Person> ret = new HashMap<Integer ,Person> ();
+ public Map<Integer ,Throwable> errors = new HashMap<Integer ,Throwable> ();
+ public boolean exd = false;
+ public void onreturn(Person msg, Integer id) {
+ System.out.println("onNotify:"+msg);
+ ret.put(id, msg);
+ }
+ public void onthrow(Throwable ex, Integer id) {
+ errors.put(id, ex);
+// ex.printStackTrace();
+ }
+ public void oninvoke(Integer id) {
+ inv.add(id);
+ }
+ }
+
+ interface IDemoService{
+ public Person get(int id);
+ }
+
+ class NormalDemoService implements IDemoService {
+ public Person get(int id){
+ return new Person(id, "charles", 4);
+ }
+ }
+ class ExceptionDemoExService implements IDemoService {
+ public Person get(int id){
+ throw new RuntimeException("request persion id is :"+ id);
+ }
+ }
+
+ public static class Person implements Serializable{
+ private static final long serialVersionUID = 1L;
+ public Person(int id, String name, int age) {
+ this.id = id;
+ this.name = name;
+ this.age = age;
+ }
+ private int id;
+ private String name ;
+ private int age;
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public int getAge() {
+ return age;
+ }
+ public void setAge(int age) {
+ this.age = age;
+ }
+ public int getId() {
+ return id;
+ }
+ public void setId(int id) {
+ this.id = id;
+ }
+ @Override
+ public String toString() {
+ return "Person [name=" + name + ", age=" + age + "]";
+ }
+ }
+ //================================================================================================
+ IDemoService demoProxy = null;
+
+ @Test
+ public void test_CloseCallback() throws Exception {
+ initOrResetUrl(false);
+ initOrResetService() ;
+ Person ret = demoProxy.get(1);
+ Assert.assertEquals(1, ret.getId());
+ destroyService();
+ }
+
+ @Test
+ public void test_Sync_Onreturn() throws Exception {
+ initOrResetUrl(false);
+ initImplicitCallBackURL_onlyOnreturn();
+ initOrResetService() ;
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(requestId, ret.getId());
+ for (int i = 0; i < 10; i++) {
+ if (! notify.ret.containsKey(requestId)){
+ Thread.sleep(200);
+ }else {
+ break;
+ }
+ }
+ Assert.assertEquals(requestId, notify.ret.get(requestId).getId());
+ destroyService();
+ }
+
+ @Test
+ public void test_Ex_OnReturn() throws Exception {
+ initOrResetUrl(true);
+ initImplicitCallBackURL_onlyOnreturn();
+
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ for (int i = 0; i < 10; i++) {
+ if (! notify.errors.containsKey(requestId)){
+ Thread.sleep(200);
+ }else {
+ break;
+ }
+ }
+ Assert.assertTrue(! notify.errors.containsKey(requestId));
+ destroyService();
+ }
+
+ @Test
+ public void test_Ex_OnInvoke() throws Exception {
+ initOrResetUrl(true);
+ initImplicitCallBackURL_onlyOninvoke();
+
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ for (int i = 0; i < 10; i++) {
+ if (! notify.inv.contains(requestId)){
+ Thread.sleep(200);
+ }else {
+ break;
+ }
+ }
+ Assert.assertTrue(notify.inv.contains(requestId));
+ destroyService();
+ }
+
+ @Test
+ public void test_Ex_Onthrow() throws Exception {
+ initOrResetUrl(true);
+ initImplicitCallBackURL_onlyOnthrow();
+
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ for (int i = 0; i < 10; i++) {
+ if (! notify.errors.containsKey(requestId)){
+ Thread.sleep(200);
+ }else {
+ break;
+ }
+ }
+ Assert.assertTrue(notify.errors.containsKey(requestId));
+ Assert.assertTrue(notify.errors.get(requestId) instanceof Throwable);
+ destroyService();
+ }
+
+ @Test
+ public void test_Sync_NoFuture() throws Exception {
+ initOrResetUrl(false);
+ initImplicitCallBackURL_onlyOnreturn();
+ destroyService();
+ exportService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(requestId, ret.getId());
+ Future<Person> pFuture = RpcContext.getContext().getFuture();
+ Assert.assertEquals(null, pFuture);
+ destroyService();
+ }
+
+ @Test
+ public void test_Async_Future() throws Exception {
+ initOrResetUrl(true);
+ destroyService();
+ exportService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ Future<Person> pFuture = RpcContext.getContext().getFuture();
+ ret = pFuture.get(1000, TimeUnit.MICROSECONDS);
+ Assert.assertEquals(requestId, ret.getId());
+ destroyService();
+ }
+
+ @Test
+ public void test_Async_Future_Multi() throws Exception {
+ initOrResetUrl(true);
+ destroyService();
+ exportService();
+ referService();
+
+ int requestId1 = 1;
+ Person ret = demoProxy.get(requestId1);
+ Assert.assertEquals(null, ret);
+ Future<Person> p1Future = RpcContext.getContext().getFuture();
+
+ int requestId2 = 1;
+ Person ret2 = demoProxy.get(requestId2);
+ Assert.assertEquals(null, ret2);
+ Future<Person> p2Future = RpcContext.getContext().getFuture();
+
+ ret = p1Future.get(1000, TimeUnit.MICROSECONDS);
+ ret2 = p2Future.get(1000, TimeUnit.MICROSECONDS);
+ Assert.assertEquals(requestId1, ret.getId());
+ Assert.assertEquals(requestId2, ret.getId());
+ destroyService();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void test_Async_Future_Ex() throws Exception {
+ try{
+ initOrResetUrl(true);
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ Future<Person> pFuture = RpcContext.getContext().getFuture();
+ ret = pFuture.get(1000, TimeUnit.MICROSECONDS);
+ Assert.assertEquals(requestId, ret.getId());
+ }finally{
+ destroyService();
+ }
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void test_Normal_Ex() throws Exception {
+ initOrResetUrl(false);
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(requestId, ret.getId());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/MultiThreadTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/MultiThreadTest.java
new file mode 100644
index 0000000..5ca2070
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/MultiThreadTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.TestCase;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl;
+
+public class MultiThreadTest extends TestCase
+{
+
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public void testDubboMultiThreadInvoke() throws Exception
+ {
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, URL.valueOf("dubbo://127.0.0.1:20880/TestService")));
+
+ final AtomicInteger counter = new AtomicInteger();
+ final DemoService service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:20880/TestService")));
+ assertEquals(service.getSize(new String[]{"123", "456", "789"}), 3);
+
+ final StringBuffer sb = new StringBuffer();
+ for(int i=0;i<1024*64+32;i++)
+ sb.append('A');
+ assertEquals(sb.toString(), service.echo(sb.toString()));
+
+ ExecutorService exec = Executors.newFixedThreadPool(10);
+ for(int i=0;i<10;i++)
+ {
+ final int fi = i;
+ exec.execute(new Runnable(){
+ public void run()
+ {
+ for(int i=0;i<30;i++)
+ {
+ System.out.println(fi+":"+counter.getAndIncrement());
+ assertEquals(service.echo(sb.toString()), sb.toString());
+ }
+ }
+ });
+ }
+ exec.shutdown();
+ exec.awaitTermination(10, TimeUnit.SECONDS);
+ rpcExporter.unexport();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java
new file mode 100644
index 0000000..4dee1b4
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.lang.reflect.Field;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.DubboAppender;
+import com.alibaba.dubbo.common.utils.LogUtil;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+public class ReferenceCountExchangeClientTest {
+
+ Exporter<?> demoExporter ;
+ Exporter<?> helloExporter ;
+
+ Invoker<IDemoService> demoServiceInvoker;
+ Invoker<IHelloService> helloServiceInvoker;
+
+ IDemoService demoService ;
+ IHelloService helloService;
+
+ ExchangeClient demoClient ;
+ ExchangeClient helloClient ;
+
+ String errorMsg = "safe guard client , should not be called ,must have a bug";
+
+ public interface IDemoService{
+ public String demo();
+ }
+ public class DemoServiceImpl implements IDemoService{
+ public String demo(){
+ return "demo";
+ }
+ }
+ public interface IHelloService{
+ public String hello();
+ }
+ public class HelloServiceImpl implements IHelloService{
+ public String hello(){
+ return "hello";
+ }
+ }
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ /**
+ * 测试共享连接
+ */
+ @Test
+ public void test_share_connect(){
+ init(0);
+ Assert.assertEquals(demoClient.getLocalAddress(), helloClient.getLocalAddress());
+ Assert.assertEquals(demoClient, helloClient);
+ destoy();
+ }
+
+ /**
+ * 测试不共享连接
+ */
+ @Test
+ public void test_not_share_connect(){
+ init(1);
+ Assert.assertNotSame(demoClient.getLocalAddress(), helloClient.getLocalAddress());
+ Assert.assertNotSame(demoClient, helloClient);
+ destoy();
+ }
+
+ /**
+ * 测试invoker多次destory不会导致计数器多次减少
+ */
+ @Test
+ public void test_multi_destory(){
+ init(0);
+ DubboAppender.doStart();
+ DubboAppender.clear();
+ demoServiceInvoker.destroy();
+ demoServiceInvoker.destroy();
+ Assert.assertEquals("hello", helloService.hello());
+ Assert.assertEquals("should not warning message", 0, LogUtil.findMessage(errorMsg));
+ LogUtil.checkNoError();
+ DubboAppender.doStop();
+ destoy();
+ }
+
+ /**
+ * 测试计数器错误,调用成功
+ */
+ @Test
+ public void test_counter_error(){
+ init(0);
+ DubboAppender.doStart();
+ DubboAppender.clear();
+
+ ReferenceCountExchangeClient client = getReferenceClient(helloServiceInvoker);
+ //close一次,计数器从2减少到1,不能warning
+ client.close();
+ Assert.assertEquals("hello", helloService.hello());
+ Assert.assertEquals("should not warning message", 0, LogUtil.findMessage(errorMsg));
+ //计数器错误,调用正常
+ client.close();
+ Assert.assertEquals("hello", helloService.hello());
+ Assert.assertEquals("should warning message", 1, LogUtil.findMessage(errorMsg));
+
+ //调用5千次输出一个错误
+ Assert.assertEquals("hello", helloService.hello());
+ Assert.assertEquals("should warning message", 1, LogUtil.findMessage(errorMsg));
+
+ DubboAppender.doStop();
+
+ //重新调用一次后status已经是available.
+ Assert.assertEquals("client status available", true, helloServiceInvoker.isAvailable());
+
+ client.close();
+ //client已经被替换为lazyclient lazy client从referenceclientmap中获取,获取到的是上次的client(已经被调用过一次),所以close状态为false
+ Assert.assertEquals("client status close", false, client.isClosed());
+ Assert.assertEquals("client status close", false, helloServiceInvoker.isAvailable());
+ destoy();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void init(int connections){
+ int port = NetUtils.getAvailablePort();
+ URL demoUrl = URL.valueOf("dubbo://127.0.0.1:"+port+"/demo?"+Constants.CONNECTIONS_KEY+"="+connections);
+ URL helloUrl = URL.valueOf("dubbo://127.0.0.1:"+port+"/hello?"+Constants.CONNECTIONS_KEY+"="+connections);
+
+ demoExporter = export(new DemoServiceImpl(), IDemoService.class, demoUrl);
+ helloExporter = export(new HelloServiceImpl(), IHelloService.class, helloUrl);
+
+ demoServiceInvoker = (Invoker<IDemoService>) referInvoker(IDemoService.class, demoUrl);
+ demoService = proxy.getProxy(demoServiceInvoker);
+ Assert.assertEquals("demo", demoService.demo());
+
+ helloServiceInvoker = (Invoker<IHelloService>) referInvoker(IHelloService.class, helloUrl);
+ helloService = proxy.getProxy(helloServiceInvoker);
+ Assert.assertEquals("hello", helloService.hello());
+
+ demoClient = getClient(demoServiceInvoker);
+ helloClient = getClient(helloServiceInvoker);
+ }
+
+ private void destoy(){
+ demoServiceInvoker.destroy();
+ helloServiceInvoker.destroy();
+ demoExporter.getInvoker().destroy();
+ helloExporter.getInvoker().destroy();
+ }
+
+ private ExchangeClient getClient(Invoker<?> invoker){
+ if (invoker.getUrl().getParameter(Constants.CONNECTIONS_KEY, 1) == 1){
+ return getInvokerClient(invoker);
+ } else {
+ ReferenceCountExchangeClient client = getReferenceClient(invoker);
+ try {
+ Field clientField = ReferenceCountExchangeClient.class.getDeclaredField("client");
+ clientField.setAccessible(true);
+ return (ExchangeClient) clientField.get(client);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.fail(e.getMessage());
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private ReferenceCountExchangeClient getReferenceClient(Invoker<?> invoker){
+ return (ReferenceCountExchangeClient)getInvokerClient(invoker);
+ }
+
+ private ExchangeClient getInvokerClient(Invoker<?> invoker){
+ @SuppressWarnings("rawtypes")
+ DubboInvoker dInvoker = (DubboInvoker)invoker;
+ try {
+ Field clientField = DubboInvoker.class.getDeclaredField("clients");
+ clientField.setAccessible(true);
+ ExchangeClient[] clients = (ExchangeClient[]) clientField.get(dInvoker);
+ return clients[0];
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.fail(e.getMessage());
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static DubboProtocol protocol = DubboProtocol.getDubboProtocol();
+ public static ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ public static Invoker<?> referInvoker(Class<?> type, URL url) {
+ return (Invoker<?>)protocol.refer(type, url);
+ }
+
+ public static <T> Exporter<T> export(T instance, Class<T> type, String url) {
+ return export(instance, type, URL.valueOf(url));
+ }
+
+ public static <T> Exporter<T> export(T instance, Class<T> type, URL url) {
+ return protocol.export(proxy.getInvoker(instance, type, url));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/RpcFilterTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/RpcFilterTest.java
new file mode 100644
index 0000000..fbd4de9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/RpcFilterTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import junit.framework.TestCase;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl;
+import com.alibaba.dubbo.rpc.service.EchoService;
+
+public class RpcFilterTest extends TestCase
+{
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public void testRpcFilter() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ URL url = URL.valueOf("dubbo://127.0.0.1:9010/com.alibaba.dubbo.rpc.DemoService?service.filter=echo");
+ protocol.export(proxy.getInvoker(service, DemoService.class, url));
+ service = proxy.getProxy(protocol.refer(DemoService.class, url));
+ assertEquals("123",service.echo("123"));
+ // cast to EchoService
+ EchoService echo = proxy.getProxy(protocol.refer(EchoService.class, url));
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/CustomArgument.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoRequest.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoService.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/EnumBak.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/EnumBak.java
new file mode 100644
index 0000000..478aa37b
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/EnumBak.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.support;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+public class EnumBak {
+
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ @Test
+ public void testNormal(){
+ int port = NetUtils.getAvailablePort();
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?proxy=jdk"
+ + "&interface=" + DemoService.class.getName()
+ + "&timeout=" + Integer.MAX_VALUE
+ );
+ DemoService demo = new DemoServiceImpl();
+ Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+ protocol.export(invoker);
+
+ URL consumerurl = serviceurl;
+ Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+ DemoService demoProxy = (DemoService)proxy.getProxy(reference);
+// System.out.println(demoProxy.getThreadName());
+ Assert.assertEquals((byte)-128, demoProxy.getbyte((byte)-128));
+
+// invoker.destroy();
+ reference.destroy();
+ }
+ @Ignore
+ @Test
+ public void testExportService() throws InterruptedException{
+ int port = NetUtils.getAvailablePort();
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?proxy=jdk&timeout="+Integer.MAX_VALUE
+ );
+ DemoService demo = new DemoServiceImpl();
+ Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+ protocol.export(invoker);
+ synchronized (EnumBak.class) {
+ EnumBak.class.wait();
+ }
+
+// URL consumerurl = serviceurl;
+// Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+// DemoService demoProxy = (DemoService)proxyFactory.createProxy(reference);
+//// System.out.println(demoProxy.getThreadName());
+// System.out.println("byte:"+demoProxy.getbyte((byte)-128));
+//
+// invoker.destroy();
+// reference.destroy();
+ }
+
+ @Test
+ public void testNormalEnum(){
+ int port = NetUtils.getAvailablePort();
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ DemoService demo = new DemoServiceImpl();
+ Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+ protocol.export(invoker);
+
+ URL consumerurl = serviceurl;
+ Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+ DemoService demoProxy = (DemoService)proxy.getProxy(reference);
+ Type type = demoProxy.enumlength(Type.High);
+ System.out.println(type);
+ Assert.assertEquals(Type.High, type);
+
+ invoker.destroy();
+ reference.destroy();
+ }
+// @Test
+ //测试2.0.5调用2.0.3的兼容
+ public void testEnumCompat(){
+ int port = 20880;
+ URL consumerurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+ DemoService demoProxy = (DemoService)proxy.getProxy(reference);
+ Type type = demoProxy.enumlength(Type.High);
+ System.out.println(type);
+ Assert.assertEquals(Type.High, type);
+ reference.destroy();
+ }
+// @Test
+ //测试2.0.5调用2.0.3的兼容
+ public void testGenricEnumCompat(){
+ int port = 20880;
+ URL consumerurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ Invoker<GenericService> reference = protocol.refer(GenericService.class, consumerurl);
+
+ GenericService demoProxy = (GenericService)proxy.getProxy(reference);
+ Object obj = demoProxy.$invoke("enumlength", new String[]{Type[].class.getName()}, new Object[]{new Type[]{Type.High,Type.High}});
+ System.out.println("obj---------->"+obj);
+ reference.destroy();
+ }
+
+// @Test
+ //测试2.0.5调用2.0.3的兼容 自定义类型参数中包含enum类型
+ public void testGenricCustomArg(){
+
+ int port = 20880;
+ URL consumerurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout=2000000"
+ );
+ Invoker<GenericService> reference = protocol.refer(GenericService.class, consumerurl);
+
+ GenericService demoProxy = (GenericService)proxy.getProxy(reference);
+ Map<String, Object> arg = new HashMap<String, Object>();
+ arg.put("type", "High");
+ arg.put("name", "hi");
+
+ Object obj = demoProxy.$invoke("get", new String[]{"com.alibaba.dubbo.rpc.CustomArgument"}, new Object[]{arg});
+ System.out.println("obj---------->"+obj);
+ reference.destroy();
+ }
+
+// @Ignore
+// @Test
+ public void testGenericExport() throws InterruptedException{
+ int port = NetUtils.getAvailablePort();
+ port = 20880;
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ DemoService demo = new DemoServiceImpl();
+ Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+ protocol.export(invoker);
+
+
+ //SERVER
+ Thread.sleep(Integer.MAX_VALUE);
+ }
+
+ @Test
+ public void testGenericEnum() throws InterruptedException{
+ int port = NetUtils.getAvailablePort();
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ DemoService demo = new DemoServiceImpl();
+ Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+ protocol.export(invoker);
+
+ URL consumerurl = serviceurl;
+
+ Invoker<GenericService> reference = protocol.refer(GenericService.class, consumerurl);
+
+ GenericService demoProxy = (GenericService)proxy.getProxy(reference);
+ Object obj = demoProxy.$invoke("enumlength", new String[]{Type[].class.getName()}, new Object[]{new Type[]{Type.High,Type.High}});
+ System.out.println("obj---------->"+obj);
+
+ invoker.destroy();
+ reference.destroy();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/NonSerialized.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Person.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.java
new file mode 100644
index 0000000..65f60f9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.support;
+
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * TODO Comment of ProtocolUtils
+ *
+ * @author william.liangf
+ */
+public class ProtocolUtils {
+
+ private static Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ public static ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public static <T> T refer(Class<T> type, String url) {
+ return refer(type, URL.valueOf(url));
+ }
+
+ public static <T> T refer(Class<T> type, URL url) {
+ return proxy.getProxy(protocol.refer(type, url));
+ }
+
+ public static Invoker<?> referInvoker(Class<?> type, URL url) {
+ return (Invoker<?>)protocol.refer(type, url);
+ }
+
+ public static <T> Exporter<T> export(T instance, Class<T> type, String url) {
+ return export(instance, type, URL.valueOf(url));
+ }
+
+ public static <T> Exporter<T> export(T instance, Class<T> type, URL url) {
+ return protocol.export(proxy.getInvoker(instance, type, url));
+ }
+
+ public static void closeAll() {
+ DubboProtocol.getDubboProtocol().destroy();
+ Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();
+ for (ExchangeServer server : servers) {
+ server.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteService.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteServiceImpl.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Type.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandlerTest.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandlerTest.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java
new file mode 100644
index 0000000..0904894
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.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.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://127.0.0.1:"+port+"/demo", 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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandlerTest.java b/dubbo-rpc/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/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/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandlerTest.java b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandlerTest.java
new file mode 100644
index 0000000..1bff521
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandlerTest.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 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.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://127.0.0.1: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/dubbo-rpc-default/src/test/resources/log4j.xml b/dubbo-rpc/dubbo-rpc-default/src/test/resources/log4j.xml
new file mode 100644
index 0000000..788ed66
--- /dev/null
+++ b/dubbo-rpc/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/dubbo-rpc-hessian/pom.xml b/dubbo-rpc/dubbo-rpc-hessian/pom.xml
new file mode 100644
index 0000000..669b4b7
--- /dev/null
+++ b/dubbo-rpc/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-rpc</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-rpc-hessian</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</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/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.java b/dubbo-rpc/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.java
new file mode 100644
index 0000000..6a9f7c7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+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.Constants;
+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.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.AbstractProxyProtocol;
+import com.caucho.hessian.HessianException;
+import com.caucho.hessian.client.HessianConnectionException;
+import com.caucho.hessian.client.HessianProxyFactory;
+import com.caucho.hessian.io.HessianMethodSerializationException;
+import com.caucho.hessian.server.HessianSkeleton;
+
+/**
+ * http rpc support.
+ *
+ * @author qianlei
+ */
+public class HessianProtocol extends AbstractProxyProtocol {
+
+ private final Map<String, HttpServer> serverMap = new ConcurrentHashMap<String, HttpServer>();
+
+ private final Map<String, HessianSkeleton> skeletonMap = new ConcurrentHashMap<String, HessianSkeleton>();
+
+ private HttpBinder httpBinder;
+
+ public HessianProtocol() {
+ super(HessianException.class);
+ }
+
+ public void setHttpBinder(HttpBinder httpBinder) {
+ this.httpBinder = httpBinder;
+ }
+
+ public int getDefaultPort() {
+ return 80;
+ }
+
+ private class HessianHandler implements HttpHandler {
+
+ public void handle(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+ String uri = request.getRequestURI();
+ HessianSkeleton skeleton = skeletonMap.get(uri);
+ 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);
+ }
+ }
+ }
+
+ }
+
+ protected <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException {
+ String addr = url.getIp() + ":" + url.getPort();
+ HttpServer server = serverMap.get(addr);
+ if (server == null) {
+ server = httpBinder.bind(url, new HessianHandler());
+ serverMap.put(addr, server);
+ }
+ final String path = url.getAbsolutePath();
+ HessianSkeleton skeleton = new HessianSkeleton(impl, type);
+ skeletonMap.put(path, skeleton);
+ return new Runnable() {
+ public void run() {
+ skeletonMap.remove(path);
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <T> T doRefer(Class<T> serviceType, URL url) throws RpcException {
+ HessianProxyFactory hessianProxyFactory = new HessianProxyFactory();
+ String client = url.getParameter(Constants.CLIENT_KEY, Constants.DEFAULT_HTTP_CLIENT);
+ if ("httpclient".equals(client)) {
+ hessianProxyFactory.setConnectionFactory(new HttpClientConnectionFactory());
+ } 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);
+ return (T) hessianProxyFactory.create(serviceType, url.setProtocol("http").toJavaURL(), Thread.currentThread().getContextClassLoader());
+ }
+
+ protected int getErrorCode(Throwable e) {
+ if (e instanceof HessianConnectionException) {
+ if (e.getCause() != null) {
+ Class<?> cls = e.getCause().getClass();
+ if (SocketTimeoutException.class.equals(cls)) {
+ return RpcException.TIMEOUT_EXCEPTION;
+ }
+ }
+ return RpcException.NETWORK_EXCEPTION;
+ } else if (e instanceof HessianMethodSerializationException) {
+ return RpcException.SERIALIZATION_EXCEPTION;
+ }
+ return super.getErrorCode(e);
+ }
+
+ 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/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnection.java b/dubbo-rpc/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/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/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java b/dubbo-rpc/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/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/dubbo-rpc-hessian/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/dubbo-rpc-hessian/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..2738fa9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-hessian/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocolTest.java b/dubbo-rpc/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocolTest.java
new file mode 100644
index 0000000..5939f03
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocolTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+import static org.junit.Assert.fail;
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.hessian.HessianServiceImpl.MyException;
+
+/**
+ * HessianProtocolTest
+ *
+ * @author william.liangf
+ */
+public class HessianProtocolTest {
+
+ @Test
+ public void testHessianProtocol() {
+ HessianServiceImpl server = new HessianServiceImpl();
+ Assert.assertFalse(server.isCalled());
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0");
+ Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));
+ Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);
+ HessianService client = proxyFactory.getProxy(invoker);
+ String result = client.sayHello("haha");
+ Assert.assertTrue(server.isCalled());
+ Assert.assertEquals("Hello, haha", result);
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+ @Test
+ public void testHttpClient() {
+ HessianServiceImpl server = new HessianServiceImpl();
+ Assert.assertFalse(server.isCalled());
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0&client=httpclient");
+ Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));
+ Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);
+ HessianService client = proxyFactory.getProxy(invoker);
+ String result = client.sayHello("haha");
+ Assert.assertTrue(server.isCalled());
+ Assert.assertEquals("Hello, haha", result);
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+ @Test
+ public void testTimeOut() {
+ HessianServiceImpl server = new HessianServiceImpl();
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0&timeout=10");
+ Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));
+ Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);
+ HessianService client = proxyFactory.getProxy(invoker);
+ try {
+ client.timeOut(6000);
+ fail();
+ } catch (RpcException expected) {
+ Assert.assertEquals(true, expected.isTimeout());
+ }finally{
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+ }
+
+ @Test
+ public void testCustomException() {
+ HessianServiceImpl server = new HessianServiceImpl();
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0");
+ Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));
+ Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);
+ HessianService client = proxyFactory.getProxy(invoker);
+ try {
+ client.customException();
+ fail();
+ } catch (MyException expected) {
+ }
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianService.java b/dubbo-rpc/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/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/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianServiceImpl.java b/dubbo-rpc/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/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/dubbo-rpc-injvm/pom.xml b/dubbo-rpc/dubbo-rpc-injvm/pom.xml
new file mode 100644
index 0000000..fe54829
--- /dev/null
+++ b/dubbo-rpc/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-rpc</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-rpc-injvm</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/filter/InjvmInvokerFilter.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/filter/InjvmInvokerFilter.java
new file mode 100644
index 0000000..be86956
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/filter/InjvmInvokerFilter.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.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.Activate;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+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.Protocol;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol;
+
+/**
+ * LocalInvokerFilter
+ *
+ * @author chao.liuc
+ * @author <a href="mailto:gang.lvg@alibaba-inc.com">kimi</a>
+ *
+ */
+@Activate(group = Constants.CONSUMER, order = 100000)
+public class InjvmInvokerFilter implements Filter {
+
+ private Invoker<?> localInvoker;
+
+ private static final Protocol protocol = ExtensionLoader
+ .getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation)
+ throws RpcException {
+ if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(invoker.getUrl())) {
+ Invoker<?> lInvoker = getLocalInvoker(invoker);
+ return lInvoker.invoke(invocation);
+ } else {
+ return invoker.invoke(invocation);
+ }
+ }
+
+ private Invoker<?> getLocalInvoker(Invoker<?> invoker) {
+ if (localInvoker == null) {
+ try {
+ localInvoker = protocol.refer(invoker.getInterface(), new URL(
+ Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0,
+ invoker.getUrl().getParameter(Constants.INTERFACE_KEY),
+ invoker.getUrl().getParameters()));
+ } catch (Throwable e) { /* ignore */
+ }
+ }
+ return localInvoker;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-rpc/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/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/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmInvoker.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmInvoker.java
new file mode 100644
index 0000000..742c57a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmInvoker.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.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;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ InjvmExporter<?> exporter = (InjvmExporter<?>) exporterMap.get(key);
+ if (exporter == null) {
+ return false;
+ } else {
+ return super.isAvailable();
+ }
+ }
+
+ 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/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.java
new file mode 100644
index 0000000..7deac08
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.injvm;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;
+
+/**
+ * InjvmProtocol
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class InjvmProtocol extends AbstractProtocol implements Protocol {
+
+ public static final String NAME = Constants.LOCAL_PROTOCOL;
+
+ public static final int DEFAULT_PORT = 0;
+
+ public int getDefaultPort() {
+ return DEFAULT_PORT;
+ }
+
+ private static InjvmProtocol INSTANCE;
+
+ public InjvmProtocol() {
+ INSTANCE = this;
+ }
+
+ public static InjvmProtocol getInjvmProtocol() {
+ if (INSTANCE == null) {
+ ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(InjvmProtocol.NAME); // load
+ }
+ return INSTANCE;
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
+ }
+
+ public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
+ return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
+ }
+
+ private boolean isExported(String key) {
+ return exporterMap != null && exporterMap.containsKey(key);
+ }
+
+ public boolean isInjvmRefer(URL url) {
+ final boolean isJvmRefer;
+ String scope = url.getParameter(Constants.SCOPE_KEY);
+ //本身已经是jvm协议了,走正常流程就是了.
+ if (Constants.LOCAL_PROTOCOL.toString().equals(url.getProtocol())) {
+ isJvmRefer = false;
+ } else if (Constants.SCOPE_LOCAL.equals(scope) || (url.getParameter("injvm", false))) {
+ //如果声明为本地引用
+ //scope=local || injvm=true 等价 injvm标签未来废弃掉.
+ isJvmRefer = true;
+ } else if (Constants.SCOPE_REMOTE.equals(scope)){
+ //声明了是远程引用,则不做本地引用
+ isJvmRefer = false;
+ } else if (url.getParameter(Constants.GENERIC_KEY, false)){
+ //泛化调用不走本地
+ isJvmRefer = false;
+ } else if (isExported(url.getServiceKey())) {
+ //默认情况下如果本地有服务暴露,则引用本地服务.
+ isJvmRefer = true;
+ } else {
+ isJvmRefer = false;
+ }
+ return isJvmRefer;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..fa3ba64
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+injvm=com.alibaba.dubbo.rpc.filter.InjvmInvokerFilter
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..09a526c
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoRequest.java b/dubbo-rpc/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/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/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoService.java b/dubbo-rpc/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/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/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoServiceImpl.java b/dubbo-rpc/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/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/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/IEcho.java b/dubbo-rpc/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/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/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/InjvmProtocolTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/InjvmProtocolTest.java
new file mode 100644
index 0000000..727d17b
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/InjvmProtocolTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protococol.injvm;
+
+
+import static junit.framework.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+/**
+ * <code>ProxiesTest</code>
+ */
+
+public class InjvmProtocolTest
+{
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ @Test
+ public void testLocalProtocol() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService")));
+ assertEquals(service.getSize(new String[]{"", "", ""}), 3);
+ service.invoke("injvm://127.0.0.1/TestService", "invoke");
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/ProtocolTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/ProtocolTest.java
new file mode 100644
index 0000000..70787f7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/ProtocolTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protococol.injvm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+/**
+ * @author ding.lid
+ */
+public class ProtocolTest {
+
+ IEcho echo = new IEcho() {
+ public String echo(String e) {
+ return e;
+ }
+ };
+
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("javassist");
+
+ URL url = URL.valueOf("injvm://localhost:0/com.alibaba.dubbo.rpc.support.IEcho");
+
+ Invoker<IEcho> invoker = proxyFactory.getInvoker(echo, IEcho.class, url);
+
+ @Test
+ public void test_destroyWontCloseAllProtocol() throws Exception {
+ Protocol autowireProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ Protocol InjvmProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("injvm");
+
+ InjvmProtocol.export(invoker);
+
+ Invoker<IEcho> refer = InjvmProtocol.refer(IEcho.class, url);
+ IEcho echoProxy = proxyFactory.getProxy(refer);
+
+ assertEquals("ok", echoProxy.echo("ok"));
+
+ try {
+ autowireProtocol.destroy();
+ } catch (UnsupportedOperationException expected) {
+ assertThat(expected.getMessage(), containsString("of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"));
+ }
+
+ assertEquals("ok2", echoProxy.echo("ok2"));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/Type.java b/dubbo-rpc/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/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/dubbo-rpc-rmi/pom.xml b/dubbo-rpc/dubbo-rpc-rmi/pom.xml
new file mode 100644
index 0000000..3b7dbdc
--- /dev/null
+++ b/dubbo-rpc/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-rpc</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-rpc-rmi</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</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/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.java b/dubbo-rpc/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.java
new file mode 100644
index 0000000..79c8a83
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.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.rmi;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.rmi.RemoteException;
+
+import org.springframework.remoting.RemoteAccessException;
+import org.springframework.remoting.rmi.RmiProxyFactoryBean;
+import org.springframework.remoting.rmi.RmiServiceExporter;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.AbstractProxyProtocol;
+
+/**
+ * RmiProtocol.
+ *
+ * @author qian.lei
+ */
+public class RmiProtocol extends AbstractProxyProtocol {
+
+ public static final int DEFAULT_PORT = 1099;
+
+ public RmiProtocol() {
+ super(RemoteAccessException.class, RemoteException.class);
+ }
+
+ public int getDefaultPort() {
+ return DEFAULT_PORT;
+ }
+
+ protected <T> Runnable doExport(final T impl, Class<T> type, URL url) throws RpcException {
+ final RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
+ rmiServiceExporter.setRegistryPort(url.getPort());
+ rmiServiceExporter.setServiceName(url.getPath());
+ rmiServiceExporter.setServiceInterface(type);
+ rmiServiceExporter.setService(impl);
+ try {
+ rmiServiceExporter.afterPropertiesSet();
+ } catch (RemoteException e) {
+ throw new RpcException(e.getMessage(), e);
+ }
+ return new Runnable() {
+ public void run() {
+ try {
+ rmiServiceExporter.destroy();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <T> T doRefer(final Class<T> serviceType, final URL url) throws RpcException {
+ final RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
+ rmiProxyFactoryBean.setServiceUrl(url.toIdentityString());
+ rmiProxyFactoryBean.setServiceInterface(serviceType);
+ rmiProxyFactoryBean.setCacheStub(true);
+ rmiProxyFactoryBean.setLookupStubOnStartup(true);
+ rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);
+ rmiProxyFactoryBean.afterPropertiesSet();
+ return (T) rmiProxyFactoryBean.getObject();
+ }
+
+ protected int getErrorCode(Throwable e) {
+ if (e instanceof RemoteAccessException) {
+ e = e.getCause();
+ }
+ if (e != null && e.getCause() != null) {
+ Class<?> cls = e.getCause().getClass();
+ // 是根据测试Case发现的问题,对RpcException.setCode进行设置
+ if (SocketTimeoutException.class.equals(cls)) {
+ return RpcException.TIMEOUT_EXCEPTION;
+ } else if (IOException.class.isAssignableFrom(cls)) {
+ return RpcException.NETWORK_EXCEPTION;
+ } else if (ClassNotFoundException.class.isAssignableFrom(cls)) {
+ return RpcException.SERIALIZATION_EXCEPTION;
+ }
+ }
+ return super.getErrorCode(e);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-rmi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/dubbo-rpc-rmi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..71466d9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-rmi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoService.java b/dubbo-rpc/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/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/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoServiceImpl.java b/dubbo-rpc/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/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/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteService.java b/dubbo-rpc/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/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/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java b/dubbo-rpc/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/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/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocolTest.java b/dubbo-rpc/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocolTest.java
new file mode 100644
index 0000000..20c13fc
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocolTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi;
+
+
+import static junit.framework.Assert.assertEquals;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.service.EchoService;
+
+public class RmiProtocolTest
+{
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+
+ public static interface NonStdRmiInterface {
+ void bark();
+ }
+ /*
+ @Test
+ public void test_getRemoteClass() throws Exception {
+ Class<NonStdRmiInterface> clazz = RmiProtocol.getRemoteClass(NonStdRmiInterface.class);
+ assertEquals(clazz, RmiProtocol.getRemoteClass(NonStdRmiInterface.class));
+ }
+ */
+ @Test
+ public void testRmiProtocolTimeout() throws Exception
+ {
+ System.setProperty("sun.rmi.transport.tcp.responseTimeout", "1000");
+ DemoService service = new DemoServiceImpl();
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));
+ try {
+ try {
+ service.throwTimeout();
+ } catch (RpcException e) {
+ assertEquals(true, e.isTimeout());
+ assertEquals(true, e.getMessage().contains("Read timed out"));
+ }
+ } finally {
+ rpcExporter.unexport();
+ }
+ }
+
+ @Test
+ public void testRmiProtocol() throws Exception
+ {
+ {
+ DemoService service = new DemoServiceImpl();
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));
+
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));
+ assertEquals(service.getSize(null), -1);
+ assertEquals(service.getSize(new String[]{"", "", ""}), 3);
+ Object result = service.invoke("rmi://127.0.0.1:9001/TestService", "invoke");
+ assertEquals("rmi://127.0.0.1:9001/TestService:invoke", result);
+
+ rpcExporter.unexport();
+ }
+
+ {
+ RemoteService remoteService = new RemoteServiceImpl();
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(remoteService, RemoteService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));
+
+ remoteService = proxy.getProxy(protocol.refer(RemoteService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));
+ remoteService.getThreadName();
+ for(int i=0;i<100;i++) {
+ String say = remoteService.sayHello("abcd");
+ assertEquals("hello abcd@" + RemoteServiceImpl.class.getName(), say);
+ }
+ rpcExporter.unexport();
+ }
+ }
+
+ // FIXME RMI协议目前的实现不支持转型成 EchoService
+ @Ignore
+ @Test
+ public void testRmiProtocol_echoService() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:9002/TestService")));
+
+ // cast to EchoService
+ EchoService echo = proxy.getProxy(protocol.refer(EchoService.class, URL.valueOf("rmi://127.0.0.1:9002/TestService")));
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+
+ rpcExporter.unexport();
+
+ RemoteService remoteService = new RemoteServiceImpl();
+ rpcExporter = protocol.export(proxy.getInvoker(remoteService, RemoteService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));
+
+ // cast to EchoService
+ echo = proxy.getProxy(protocol.refer(EchoService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+
+ rpcExporter.unexport();
+ }
+
+ /*@Test
+ public void testRpcInvokerGroup() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ RpcUtils.export("demo://127.0.0.1:9030/com.alibaba.dubbo.rpc.TestService",DemoService.class,service);
+ RpcUtils.export("dubbo://127.0.0.1:9031/TestService",DemoService.class,service);
+ RpcUtils.export("rmi://127.0.0.1:9032/com.alibaba.dubbo.rpc.TestService",DemoService.class,service);
+ RpcUtils.export("rmi://127.0.0.1:9033/com.alibaba.dubbo.rpc.TestService",DemoService.class,service);
+
+ service = RpcUtils.createProxy(DemoService.class,
+ new String[]{
+ "demo://127.0.0.1:9030/com.alibaba.dubbo.rpc.TestService?weight=20",
+ "dubbo://127.0.0.1:9031/TestService?weight=20",
+ "rmi://127.0.0.1:9032/com.alibaba.dubbo.rpc.TestService",
+ });
+ assertEquals(service.getSize(null), -1);
+ assertEquals(service.getSize(new String[]{"","",""}), 3);
+
+ // cast to EchoService
+ EchoService echo = RpcUtils.createProxy(EchoService.class,
+ new String[]{
+ "demo://127.0.0.1:9030/com.alibaba.dubbo.rpc.TestService?weight=20",
+ "dubbo://127.0.0.1:9031/TestService?weight=20",
+ "rmi://127.0.0.1:9032/com.alibaba.dubbo.rpc.TestService",
+ });
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+ }*/
+
+ /*public void testForkInvoke() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.createInvoker("dubbo://127.0.0.1:9040/TestService", DemoService.class, service);
+ protocol.export(proxy.createInvoker("dubbo://127.0.0.1:9041/TestService", DemoService.class, service);
+ protocol.export(proxy.createInvoker("rmi://127.0.0.1:9042/com.alibaba.dubbo.rpc.TestService", DemoService.class, service);
+ protocol.export(proxy.createInvoker("rmi://127.0.0.1:9043/com.alibaba.dubbo.rpc.TestService", DemoService.class, service);
+
+ RpcInvokerGroup group = Proxies.createInvoker(DemoService.class, new String[]{
+ "dubbo://127.0.0.1:9040/TestService",
+ "dubbo://127.0.0.1:9041/TestService",
+ "rmi://127.0.0.1:9042/com.alibaba.dubbo.rpc.TestService",
+ "rmi://127.0.0.1:9043/com.alibaba.dubbo.rpc.TestService",
+ });
+ group.getMethodSettings("echo").setFork(true);
+ group.getMethodSettings("echo").setForkInvokeCallback(new ForkInvokeCallback(){
+ public Object merge(RpcInvocation invocation, RpcResult[] results) throws Throwable
+ {
+ System.out.println("merge result begin:");
+ for( RpcResult result : results )
+ {
+ if( result.hasException() )
+ System.out.println("exception:"+result.getException().getMessage());
+ else
+ System.out.println("result:"+result.getResult());
+ }
+ System.out.println("merge result end:");
+ return "aaaa";
+ }
+ });
+
+ service = proxy.createProxy(protocol.refer(DemoService.class, group);
+ service.echo("test");
+
+ // cast to EchoService
+ EchoService echo = proxy.createProxy(protocol.refer(EchoService.class, group);
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+ }*/
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/Type.java b/dubbo-rpc/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/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..39b50b6
--- /dev/null
+++ b/dubbo-rpc/pom.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.
+-->
+<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.2.2</version>
+ </parent>
+ <artifactId>dubbo-rpc</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The rpc module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-rpc-api</module>
+ <module>dubbo-rpc-default</module>
+ <module>dubbo-rpc-injvm</module>
+ <module>dubbo-rpc-rmi</module>
+ <module>dubbo-rpc-hessian</module>
+ </modules>
+</project>
diff --git a/dubbo-simple/dubbo-monitor-simple/pom.xml b/dubbo-simple/dubbo-monitor-simple/pom.xml
new file mode 100644
index 0000000..691df22
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/pom.xml
@@ -0,0 +1,92 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-simple</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-monitor-simple</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The simple monitor module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>jfree</groupId>
+ <artifactId>jfreechart</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.zookeeper</groupId>
+ <artifactId>zookeeper</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack</id>
+ <phase>package</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo</artifactId>
+ <version>${project.parent.version}</version>
+ <outputDirectory>${project.build.directory}/dubbo</outputDirectory>
+ <includes>META-INF/assembly/**</includes>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptor>src/main/assembly/assembly.xml</descriptor>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/assembly/assembly.xml b/dubbo-simple/dubbo-monitor-simple/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..25235d9
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-monitor-simple/src/main/assembly/conf/dubbo.properties b/dubbo-simple/dubbo-monitor-simple/src/main/assembly/conf/dubbo.properties
new file mode 100644
index 0000000..5aa8946
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/assembly/conf/dubbo.properties
@@ -0,0 +1,29 @@
+##
+# Copyright 1999-2011 Alibaba Group.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+dubbo.container=log4j,spring,registry,jetty
+dubbo.application.name=simple-monitor
+dubbo.application.owner=
+dubbo.registry.address=multicast://224.5.6.7:1234
+#dubbo.registry.address=zookeeper://127.0.0.1:2181
+#dubbo.registry.address=redis://127.0.0.1:6379
+#dubbo.registry.address=dubbo://127.0.0.1:9090
+dubbo.protocol.port=7070
+dubbo.jetty.port=8080
+dubbo.jetty.directory=${user.home}/monitor
+dubbo.charts.directory=${dubbo.jetty.directory}/charts
+dubbo.statistics.directory=${user.home}/monitor/statistics
+dubbo.log4j.file=logs/dubbo-monitor-simple.log
+dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/CountUtils.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/CountUtils.java
new file mode 100644
index 0000000..4604e00
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/RegistryContainer.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/RegistryContainer.java
new file mode 100644
index 0000000..9db3a04
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/RegistryContainer.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.Container;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * RegistryContainer
+ *
+ * @author william.liangf
+ */
+public class RegistryContainer implements Container {
+
+ private static final Logger logger = LoggerFactory.getLogger(RegistryContainer.class);
+
+ public static final String REGISTRY_ADDRESS = "dubbo.registry.address";
+
+ private final RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
+
+ private final Set<String> applications = new ConcurrentHashSet<String>();
+
+ private final Map<String, Set<String>> providerServiceApplications = new ConcurrentHashMap<String, Set<String>>();
+
+ private final Map<String, Set<String>> providerApplicationServices = new ConcurrentHashMap<String, Set<String>>();
+
+ private final Map<String, Set<String>> consumerServiceApplications = new ConcurrentHashMap<String, Set<String>>();
+
+ private final Map<String, Set<String>> consumerApplicationServices = new ConcurrentHashMap<String, Set<String>>();
+
+ private final Set<String> services = new ConcurrentHashSet<String>();
+
+ private final Map<String, List<URL>> serviceProviders = new ConcurrentHashMap<String, List<URL>>();
+
+ private final Map<String, List<URL>> serviceConsumers = new ConcurrentHashMap<String, List<URL>>();
+
+ private 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 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.ADMIN_PROTOCOL, NetUtils.getLocalHost(), 0, "",
+ Constants.INTERFACE_KEY, Constants.ANY_VALUE,
+ Constants.GROUP_KEY, Constants.ANY_VALUE,
+ Constants.VERSION_KEY, Constants.ANY_VALUE,
+ Constants.CLASSIFIER_KEY, Constants.ANY_VALUE,
+ Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + ","
+ + Constants.CONSUMERS_CATEGORY,
+ Constants.CHECK_KEY, String.valueOf(false));
+ registry.subscribe(subscribeUrl, new NotifyListener() {
+ public void notify(List<URL> urls) {
+ if (urls == null || urls.size() == 0) {
+ return;
+ }
+ Map<String, List<URL>> proivderMap = new HashMap<String, List<URL>>();
+ Map<String, List<URL>> consumerMap = new HashMap<String, List<URL>>();
+ for (URL url : urls) {
+ String application = url.getParameter(Constants.APPLICATION_KEY);
+ if (application != null && application.length() > 0) {
+ applications.add(application);
+ }
+ String service = url.getServiceInterface();
+ services.add(service);
+ String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
+ if (Constants.PROVIDERS_CATEGORY.equals(category)) {
+ if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
+ serviceProviders.remove(service);
+ } else {
+ 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);
+ }
+ }
+ } else if (Constants.CONSUMERS_CATEGORY.equals(category)) {
+ if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
+ serviceConsumers.remove(service);
+ } else {
+ 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);
+ }
+
+ }
+ }
+ }
+ if (proivderMap != null && proivderMap.size() > 0) {
+ serviceProviders.putAll(proivderMap);
+ }
+ if (consumerMap != null && consumerMap.size() > 0) {
+ serviceConsumers.putAll(consumerMap);
+ }
+ }
+ });
+ }
+
+ public void stop() {
+ try {
+ registry.destroy();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorService.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorService.java
new file mode 100644
index 0000000..ba3d0f0
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorService.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import javax.imageio.ImageIO;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.DateAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.time.Minute;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.monitor.MonitorService;
+
+/**
+ * SimpleMonitorService
+ *
+ * @author william.liangf
+ */
+public class SimpleMonitorService implements MonitorService {
+
+ private static final Logger logger = LoggerFactory.getLogger(SimpleMonitorService.class);
+
+ private static final String[] types = {SUCCESS, FAILURE, ELAPSED, CONCURRENT, MAX_ELAPSED, MAX_CONCURRENT};
+
+ private static final String POISON_PROTOCOL = "poison";
+
+ // 定时任务执行器
+ private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboMonitorTimer", true));
+
+ // 图表绘制定时器
+ private final ScheduledFuture<?> chartFuture;
+
+ private final Thread writeThread;
+
+ private final BlockingQueue<URL> queue;
+
+ private String statisticsDirectory = "statistics";
+
+ private String chartsDirectory = "charts";
+
+ private volatile boolean running = true;
+
+ private static SimpleMonitorService INSTANCE = null;
+
+ public static SimpleMonitorService getInstance() {
+ return INSTANCE;
+ }
+
+ public String getStatisticsDirectory() {
+ return statisticsDirectory;
+ }
+
+ public void setStatisticsDirectory(String statistics) {
+ if (statistics != null) {
+ this.statisticsDirectory = statistics;
+ }
+ }
+
+ public String getChartsDirectory() {
+ return chartsDirectory;
+ }
+
+ public void setChartsDirectory(String charts) {
+ if (charts != null) {
+ this.chartsDirectory = charts;
+ }
+ }
+
+ public SimpleMonitorService() {
+ queue = new LinkedBlockingQueue<URL>(Integer.parseInt(ConfigUtils.getProperty("dubbo.monitor.queue", "100000")));
+ writeThread = new Thread(new Runnable() {
+ public void run() {
+ while (running) {
+ try {
+ write(); // 记录统计日志
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected error occur at write stat log, cause: " + t.getMessage(), t);
+ try {
+ Thread.sleep(5000); // 失败延迟
+ } catch (Throwable t2) {
+ }
+ }
+ }
+ }
+ });
+ writeThread.setDaemon(true);
+ writeThread.setName("DubboMonitorAsyncWriteLogThread");
+ writeThread.start();
+ chartFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
+ public void run() {
+ try {
+ draw(); // 绘制图表
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected error occur at draw stat chart, cause: " + t.getMessage(), t);
+ }
+ }
+ }, 1, 300, TimeUnit.SECONDS);
+ INSTANCE = this;
+ }
+
+ public void close() {
+ try {
+ running = false;
+ queue.offer(new URL(POISON_PROTOCOL, NetUtils.LOCALHOST, 0));
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ try {
+ chartFuture.cancel(true);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+
+ private void write() throws Exception {
+ URL statistics = queue.take();
+ if (POISON_PROTOCOL.equals(statistics.getProtocol())) {
+ return;
+ }
+ String timestamp = statistics.getParameter(Constants.TIMESTAMP_KEY);
+ Date now;
+ if (timestamp == null || timestamp.length() == 0) {
+ now = new Date();
+ } else if (timestamp.length() == "yyyyMMddHHmmss".length()) {
+ now = new SimpleDateFormat("yyyyMMddHHmmss").parse(timestamp);
+ } else {
+ now = new Date(Long.parseLong(timestamp));
+ }
+ String day = new SimpleDateFormat("yyyyMMdd").format(now);
+ SimpleDateFormat format = new SimpleDateFormat("HHmm");
+ for (String key : types) {
+ try {
+ String type;
+ String consumer;
+ String provider;
+ if (statistics.hasParameter(PROVIDER)) {
+ type = CONSUMER;
+ consumer = statistics.getHost();
+ provider = statistics.getParameter(PROVIDER);
+ int i = provider.indexOf(':');
+ if (i > 0) {
+ provider = provider.substring(0, i);
+ }
+ } else {
+ type = PROVIDER;
+ consumer = statistics.getParameter(CONSUMER);
+ int i = consumer.indexOf(':');
+ if (i > 0) {
+ consumer = consumer.substring(0, i);
+ }
+ provider = statistics.getHost();
+ }
+ String filename = statisticsDirectory
+ + "/" + day
+ + "/" + statistics.getServiceInterface()
+ + "/" + statistics.getParameter(METHOD)
+ + "/" + consumer
+ + "/" + provider
+ + "/" + type + "." + key;
+ File file = new File(filename);
+ File dir = file.getParentFile();
+ if (dir != null && ! dir.exists()) {
+ dir.mkdirs();
+ }
+ FileWriter writer = new FileWriter(file, true);
+ try {
+ writer.write(format.format(now) + " " + statistics.getParameter(key, 0) + "\n");
+ writer.flush();
+ } finally {
+ writer.close();
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+
+ private void draw() {
+ File rootDir = new File(statisticsDirectory);
+ if (! rootDir.exists()) {
+ return;
+ }
+ File[] dateDirs = rootDir.listFiles();
+ for (File dateDir : dateDirs) {
+ File[] serviceDirs = dateDir.listFiles();
+ for (File serviceDir : serviceDirs) {
+ File[] methodDirs = serviceDir.listFiles();
+ for (File methodDir : methodDirs) {
+ String methodUri = chartsDirectory + "/" + dateDir.getName() + "/" + serviceDir.getName() + "/" + methodDir.getName();
+
+ File successFile = new File(methodUri + "/" + SUCCESS + ".png");
+ long successModified = successFile.lastModified();
+ boolean successChanged = false;
+ Map<String, long[]> successData = new HashMap<String, long[]>();
+ double[] successSummary = new double[4];
+
+ File elapsedFile = new File(methodUri + "/" + ELAPSED + ".png");
+ long elapsedModified = elapsedFile.lastModified();
+ boolean elapsedChanged = false;
+ Map<String, long[]> elapsedData = new HashMap<String, long[]>();
+ double[] elapsedSummary = new double[4];
+ long elapsedMax = 0;
+
+ File[] consumerDirs = methodDir.listFiles();
+ for (File consumerDir : consumerDirs) {
+ File[] providerDirs = consumerDir.listFiles();
+ for (File providerDir : providerDirs) {
+ File consumerSuccessFile = new File(providerDir, CONSUMER + "." + SUCCESS);
+ File providerSuccessFile = new File(providerDir, PROVIDER + "." + SUCCESS);
+ appendData(new File[] {consumerSuccessFile, providerSuccessFile}, successData, successSummary);
+ if (consumerSuccessFile.lastModified() > successModified
+ || providerSuccessFile.lastModified() > successModified) {
+ successChanged = true;
+ }
+
+ File consumerElapsedFile = new File(providerDir, CONSUMER + "." + ELAPSED);
+ File providerElapsedFile = new File(providerDir, PROVIDER + "." + ELAPSED);
+ appendData(new File[] {consumerElapsedFile, providerElapsedFile}, elapsedData, elapsedSummary);
+ elapsedMax = Math.max(elapsedMax, CountUtils.max(new File(providerDir, CONSUMER + "." + MAX_ELAPSED)));
+ elapsedMax = Math.max(elapsedMax, CountUtils.max(new File(providerDir, PROVIDER + "." + MAX_ELAPSED)));
+ if (consumerElapsedFile.lastModified() > elapsedModified
+ || providerElapsedFile.lastModified() > elapsedModified) {
+ elapsedChanged = true;
+ }
+ }
+ }
+ if (elapsedChanged) {
+ divData(elapsedData, successData);
+ elapsedSummary[0] = elapsedMax;
+ elapsedSummary[1] = -1;
+ elapsedSummary[2] = successSummary[3] == 0 ? 0 : elapsedSummary[3] / successSummary[3];
+ elapsedSummary[3] = -1;
+ createChart("ms/t", serviceDir.getName(), methodDir.getName(), dateDir.getName(), new String[] {CONSUMER, PROVIDER}, elapsedData, elapsedSummary, elapsedFile.getAbsolutePath());
+ }
+ if (successChanged) {
+ divData(successData, 60);
+ successSummary[0] = successSummary[0] / 60;
+ successSummary[1] = successSummary[1] / 60;
+ successSummary[2] = successSummary[2] / 60;
+ createChart("t/s", serviceDir.getName(), methodDir.getName(), dateDir.getName(), new String[] {CONSUMER, PROVIDER}, successData, successSummary, successFile.getAbsolutePath());
+ }
+ }
+ }
+ }
+ }
+
+ private void divData(Map<String, long[]> successMap, long unit) {
+ for (long[] success : successMap.values()) {
+ for (int i = 0; i < success.length; i ++) {
+ success[i] = success[i] / unit;
+ }
+ }
+ }
+
+ private void divData(Map<String, long[]> elapsedMap, Map<String, long[]> successMap) {
+ for (Map.Entry<String, long[]> entry : elapsedMap.entrySet()) {
+ long[] elapsed = entry.getValue();
+ long[] success = successMap.get(entry.getKey());
+ for (int i = 0; i < elapsed.length; i ++) {
+ elapsed[i] = success[i] == 0 ? 0 : elapsed[i] / success[i];
+ }
+ }
+ }
+
+ private void appendData(File[] files, Map<String, long[]> data, double[] summary) {
+ for (int i = 0; i < files.length; i ++) {
+ File file = files[i];
+ if (! file.exists()) {
+ continue;
+ }
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ try {
+ int sum = 0;
+ int cnt = 0;
+ String line;
+ while ((line = reader.readLine()) != null) {
+ int index = line.indexOf(" ");
+ if (index > 0) {
+ String key = line.substring(0, index).trim();
+ long value = Long.parseLong(line.substring(index + 1).trim());
+ long[] values = data.get(key);
+ if (values == null) {
+ values = new long[files.length];
+ data.put(key, values);
+ }
+ values[i] += value;
+ summary[0] = Math.max(summary[0], values[i]);
+ summary[1] = summary[1] == 0 ? values[i] : Math.min(summary[1], values[i]);
+ sum += value;
+ cnt ++;
+ }
+ }
+ if (i == 0) {
+ summary[3] += sum;
+ summary[2] = (summary[2] + (sum / cnt)) / 2;
+ }
+ } finally {
+ reader.close();
+ }
+ } catch (IOException e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+
+ private static void createChart(String key, String service, String method, String date, String[] types, Map<String, long[]> data, double[] summary, String path) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm");
+ DecimalFormat numberFormat = new DecimalFormat("###,##0.##");
+ TimeSeriesCollection xydataset = new TimeSeriesCollection();
+ for (int i = 0; i < types.length; i ++) {
+ String type = types[i];
+ TimeSeries timeseries = new TimeSeries(type);
+ for (Map.Entry<String, long[]> entry : data.entrySet()) {
+ try {
+ timeseries.add(new Minute(dateFormat.parse(date + entry.getKey())), entry.getValue()[i]);
+ } catch (ParseException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ xydataset.addSeries(timeseries);
+ }
+ JFreeChart jfreechart = ChartFactory.createTimeSeriesChart(
+ "max: " + numberFormat.format(summary[0]) + (summary[1] >=0 ? " min: " + numberFormat.format(summary[1]) : "")
+ + " avg: " + numberFormat.format(summary[2]) + (summary[3] >=0 ? " sum: " + numberFormat.format(summary[3]) : ""),
+ toDisplayService(service) + " " + method + " " + toDisplayDate(date), key, xydataset, true, true, false);
+ jfreechart.setBackgroundPaint(Color.WHITE);
+ XYPlot xyplot = (XYPlot) jfreechart.getPlot();
+ xyplot.setBackgroundPaint(Color.WHITE);
+ xyplot.setDomainGridlinePaint(Color.GRAY);
+ xyplot.setRangeGridlinePaint(Color.GRAY);
+ xyplot.setDomainGridlinesVisible(true);
+ xyplot.setRangeGridlinesVisible(true);
+ DateAxis dateaxis = (DateAxis) xyplot.getDomainAxis();
+ dateaxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
+ BufferedImage image = jfreechart.createBufferedImage(600, 300);
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("write chart: " + path);
+ }
+ File methodChartFile = new File(path);
+ File methodChartDir = methodChartFile.getParentFile();
+ if (methodChartDir != null && ! methodChartDir.exists()) {
+ methodChartDir.mkdirs();
+ }
+ FileOutputStream output = new FileOutputStream(methodChartFile);
+ try {
+ ImageIO.write(image, "png", output);
+ output.flush();
+ } finally {
+ output.close();
+ }
+ } catch (IOException e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ private static String toDisplayService(String service) {
+ int i = service.lastIndexOf('.');
+ if (i >= 0) {
+ return service.substring(i + 1);
+ }
+ return service;
+ }
+
+ private static String toDisplayDate(String date) {
+ try {
+ return new SimpleDateFormat("yyyy-MM-dd").format(new SimpleDateFormat("yyyyMMdd").parse(date));
+ } catch (ParseException e) {
+ return date;
+ }
+ }
+
+ public void count(URL statistics) {
+ collect(statistics);
+ }
+
+ public void collect(URL statistics) {
+ queue.offer(statistics);
+ if (logger.isInfoEnabled()) {
+ logger.info("collect statistics: " + statistics);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ApplicationsPageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ApplicationsPageHandler.java
new file mode 100644
index 0000000..69e0687
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ApplicationsPageHandler.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Menu;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.simple.RegistryContainer;
+
+/**
+ * ApplicationsPageHandler
+ *
+ * @author william.liangf
+ */
+@Menu(name = "Applications", desc = "Show application dependencies.", order = 1000)
+public class ApplicationsPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ Set<String> applications = RegistryContainer.getInstance().getApplications();
+ List<List<String>> rows = new ArrayList<List<String>>();
+ int providersCount = 0;
+ int consumersCount = 0;
+ int efferentCount = 0;
+ int afferentCount = 0;
+ if (applications != null && applications.size() > 0) {
+ for (String application : applications) {
+ List<String> row = new ArrayList<String>();
+ row.add(application);
+
+ List<URL> providers = RegistryContainer.getInstance().getProvidersByApplication(application);
+ List<URL> consumers = RegistryContainer.getInstance().getConsumersByApplication(application);
+
+ if (providers != null && providers.size() > 0
+ || consumers != null && consumers.size() > 0) {
+ URL provider = (providers != null && providers.size() > 0 ? providers.iterator().next() : consumers.iterator().next());
+ row.add(provider.getParameter("owner", "") + (provider.hasParameter("organization") ? " (" + provider.getParameter("organization") + ")" : ""));
+ } else {
+ row.add("");
+ }
+
+ int providersSize = providers == null ? 0 : providers.size();
+ providersCount += providersSize;
+ row.add(providersSize == 0 ? "<font color=\"blue\">No provider</font>" : "<a href=\"providers.html?application=" + application + "\">Providers(" + providersSize + ")</a>");
+
+ int consumersSize = consumers == null ? 0 : consumers.size();
+ consumersCount += consumersSize;
+ row.add(consumersSize == 0 ? "<font color=\"blue\">No consumer</font>" : "<a href=\"consumers.html?application=" + application + "\">Consumers(" + consumersSize + ")</a>");
+
+ Set<String> efferents = RegistryContainer.getInstance().getDependencies(application, false);
+ int efferentSize = efferents == null ? 0 : efferents.size();
+ efferentCount += efferentSize;
+ row.add(efferentSize == 0 ? "<font color=\"blue\">No dependency</font>" : "<a href=\"dependencies.html?application=" + application + "\">Depends On(" + efferentSize + ")</a>");
+
+ Set<String> afferents = RegistryContainer.getInstance().getDependencies(application, true);
+ int afferentSize = afferents == null ? 0 : afferents.size();
+ afferentCount += afferentSize;
+ row.add(afferentSize == 0 ? "<font color=\"blue\">No used</font>" : "<a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By(" + afferentSize + ")</a>");
+ rows.add(row);
+ }
+ }
+ return new Page("Applications", "Applications (" + rows.size() + ")",
+ new String[] { "Application Name:", "Owner", "Providers(" + providersCount + ")", "Consumers(" + consumersCount + ")", "Depends On(" + efferentCount + ")", "Used By(" + afferentCount + ")" }, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ChartsPageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ChartsPageHandler.java
new file mode 100644
index 0000000..5201d44
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ChartsPageHandler.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.monitor.simple.SimpleMonitorService;
+
+/**
+ * ChartsPageHandler
+ *
+ * @author william.liangf
+ */
+public class ChartsPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String service = url.getParameter("service");
+ if (service == null || service.length() == 0) {
+ throw new IllegalArgumentException("Please input service parameter.");
+ }
+ String date = url.getParameter("date");
+ if (date == null || date.length() == 0) {
+ date = new SimpleDateFormat("yyyyMMdd").format(new Date());
+ }
+ List<List<String>> rows = new ArrayList<List<String>>();
+ String directory = SimpleMonitorService.getInstance().getChartsDirectory();
+ File chartsDir = new File(directory);
+ String filename = directory + "/" + date + "/" + service;
+ File serviceDir = new File(filename);
+ if (serviceDir.exists()) {
+ File[] methodDirs = serviceDir.listFiles();
+ for (File methodDir : methodDirs) {
+ String methodUri = chartsDir.getName() + "/" + date + "/" + service + "/" + methodDir.getName() + "/";
+ rows.add(toRow(methodDir, methodUri));
+ }
+ }
+ StringBuilder nav = new StringBuilder();
+ nav.append("<a href=\"services.html\">Services</a> > ");
+ nav.append(service);
+ nav.append(" > <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 > <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-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ConsumersPageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ConsumersPageHandler.java
new file mode 100644
index 0000000..74d7dc2
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ConsumersPageHandler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.simple.RegistryContainer;
+
+/**
+ * ConsumersPageHandler
+ *
+ * @author william.liangf
+ */
+public class ConsumersPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String service = url.getParameter("service");
+ String host = url.getParameter("host");
+ String application = url.getParameter("application");
+ if (service != null && service.length() > 0) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<URL> consumers = RegistryContainer.getInstance().getConsumersByService(service);
+ if (consumers != null && consumers.size() > 0) {
+ for (URL u : consumers) {
+ List<String> row = new ArrayList<String>();
+ String s = u.toFullString();
+ row.add(s.replace("&", "&"));
+ row.add("<button onclick=\"if(confirm('Confirm unsubscribe consumer?')){window.location.href='unsubscribe.html?service=" + service + "&consumer=" + URL.encode(s) + "';}\">Unsubscribe</button>");
+ rows.add(row);
+ }
+ }
+ return new Page("<a href=\"services.html\">Services</a> > " + service
+ + " > <a href=\"providers.html?service=" + service
+ + "\">Providers</a> | Consumers | <a href=\"statistics.html?service=" + service
+ + "\">Statistics</a> | <a href=\"charts.html?service=" + service
+ + "\">Charts</a>", "Consumers (" + rows.size() + ")",
+ new String[] { "Consumer URL:", "Unsubscribe" }, rows);
+ } else if (host != null && host.length() > 0) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<URL> consumers = RegistryContainer.getInstance().getConsumersByHost(host);
+ if (consumers != null && consumers.size() > 0) {
+ for (URL u : consumers) {
+ List<String> row = new ArrayList<String>();
+ String s = u.toFullString();
+ row.add(s.replace("&", "&"));
+ row.add("<button onclick=\"if(confirm('Confirm unsubscribe consumer?')){window.location.href='unsubscribe.html?host=" + host + "&consumer=" + URL.encode(s) + "';}\">Unsubscribe</button>");
+ rows.add(row);
+ }
+ }
+ return new Page("<a href=\"hosts.html\">Hosts</a> > " + NetUtils.getHostName(host) + "/" + host + " > <a href=\"providers.html?host=" + host + "\">Providers</a> | Consumers", "Consumers (" + rows.size() + ")",
+ new String[] { "Consumer URL:", "Unsubscribe" }, rows);
+ } else if (application != null && application.length() > 0) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<URL> consumers = RegistryContainer.getInstance().getConsumersByApplication(application);
+ if (consumers != null && consumers.size() > 0) {
+ for (URL u : consumers) {
+ List<String> row = new ArrayList<String>();
+ String s = u.toFullString();
+ row.add(s.replace("&", "&"));
+ row.add("<button onclick=\"if(confirm('Confirm unsubscribe consumer?')){window.location.href='unsubscribe.html?application=" + application + "&consumer=" + URL.encode(s) + "';}\">Unsubscribe</button>");
+ rows.add(row);
+ }
+ }
+ return new Page("<a href=\"applications.html\">Applications</a> > " + application + " > <a href=\"providers.html?application=" + application + "\">Providers</a> | Consumers | <a href=\"dependencies.html?application=" + application + "\">Depends On</a> | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>", "Consumers (" + rows.size() + ")",
+ new String[] { "Consumer URL:", "Unsubscribe" }, rows);
+ } else {
+ throw new IllegalArgumentException("Please input service or host or application parameter.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/DependenciesPageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/DependenciesPageHandler.java
new file mode 100644
index 0000000..103b737
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/DependenciesPageHandler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.simple.RegistryContainer;
+
+/**
+ * DependenciesPageHandler
+ *
+ * @author william.liangf
+ */
+public class DependenciesPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String application = url.getParameter("application");
+ if (application == null || application.length() == 0) {
+ throw new IllegalArgumentException("Please input application parameter.");
+ }
+ boolean reverse = url.getParameter("reverse", false);
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Set<String> directly = RegistryContainer.getInstance().getDependencies(application, reverse);
+ Set<String> indirectly = new HashSet<String>();
+ appendDependency(rows, reverse, application, 0, new HashSet<String>(), indirectly);
+ indirectly.remove(application);
+ return new Page("<a href=\"applications.html\">Applications</a> > " + application +
+ " > <a href=\"providers.html?application=" + application + "\">Providers</a> | <a href=\"consumers.html?application=" + application + "\">Consumers</a> | " +
+ (reverse ? "<a href=\"dependencies.html?application=" + application + "\">Depends On</a> | Used By"
+ : "Depends On | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>"), (reverse ? "Used By" : "Depends On") + " (" + directly.size() + "/" + indirectly.size() + ")", new String[] { "Application Name:"}, rows);
+ }
+
+ private void appendDependency(List<List<String>> rows, boolean reverse, String application, int level, Set<String> appended, Set<String> indirectly) {
+ List<String> row = new ArrayList<String>();
+ StringBuilder buf = new StringBuilder();
+ if (level > 0) {
+ for (int i = 0; i < level; i ++) {
+ buf.append(" |");
+ }
+ buf.append(reverse ? "<-- " : "--> ");
+ }
+ 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-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/HostsPageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/HostsPageHandler.java
new file mode 100644
index 0000000..d298f9a
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/HostsPageHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.page.Menu;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.simple.RegistryContainer;
+
+/**
+ * HostsPageHandler
+ *
+ * @author william.liangf
+ */
+@Menu(name = "Hosts", desc = "Show provider and consumer hosts", order = 3000)
+public class HostsPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Set<String> hosts = RegistryContainer.getInstance().getHosts();
+ int providersCount = 0;
+ int consumersCount = 0;
+ if (hosts != null && hosts.size() > 0) {
+ for (String host : hosts) {
+ List<String> row = new ArrayList<String>();
+ row.add(NetUtils.getHostName(host) + "/" + host);
+
+ List<URL> providers = RegistryContainer.getInstance().getProvidersByHost(host);
+ List<URL> consumers = RegistryContainer.getInstance().getConsumersByHost(host);
+
+ if (providers != null && providers.size() > 0
+ || consumers != null && consumers.size() > 0) {
+ URL provider = (providers != null && providers.size() > 0 ? providers.iterator().next() : consumers.iterator().next());
+ row.add(provider.getParameter(Constants.APPLICATION_KEY, ""));
+ row.add(provider.getParameter("owner", "") + (provider.hasParameter("organization") ? " (" + provider.getParameter("organization") + ")" : ""));
+ } else {
+ row.add("");
+ row.add("");
+ }
+
+ int proviedSize = providers == null ? 0 : providers.size();
+ providersCount += proviedSize;
+ row.add(proviedSize == 0 ? "<font color=\"blue\">No provider</font>" : "<a href=\"providers.html?host=" + host + "\">Providers(" + proviedSize + ")</a>");
+
+ int consumersSize = consumers == null ? 0 : consumers.size();
+ consumersCount += consumersSize;
+ row.add(consumersSize == 0 ? "<font color=\"blue\">No consumer</font>" : "<a href=\"consumers.html?host=" + host + "\">Consumers(" + consumersSize + ")</a>");
+
+ rows.add(row);
+ }
+ }
+ return new Page("Hosts", "Hosts (" + rows.size() + ")",
+ new String[] { "Host Name/IP:", "Application", "Owner", "Providers(" + providersCount + ")", "Consumers(" + consumersCount + ")" }, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ProvidersPageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ProvidersPageHandler.java
new file mode 100644
index 0000000..7de660a
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ProvidersPageHandler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.simple.RegistryContainer;
+
+/**
+ * ProvidersPageHandler
+ *
+ * @author william.liangf
+ */
+public class ProvidersPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String service = url.getParameter("service");
+ String host = url.getParameter("host");
+ String application = url.getParameter("application");
+ if (service != null && service.length() > 0) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<URL> providers = RegistryContainer.getInstance().getProvidersByService(service);
+ if (providers != null && providers.size() > 0) {
+ for (URL u : providers) {
+ List<String> row = new ArrayList<String>();
+ String s = u.toFullString();
+ row.add(s.replace("&", "&"));
+ row.add("<button onclick=\"if(confirm('Confirm unregister provider?')){window.location.href='unregister.html?service=" + service + "&provider=" + URL.encode(s) + "';}\">Unregister</button>");
+ rows.add(row);
+ }
+ }
+ return new Page("<a href=\"services.html\">Services</a> > " + service
+ + " > Providers | <a href=\"consumers.html?service=" + service
+ + "\">Consumers</a> | <a href=\"statistics.html?service=" + service
+ + "\">Statistics</a> | <a href=\"charts.html?service=" + service
+ + "\">Charts</a>", "Providers (" + rows.size() + ")",
+ new String[] { "Provider URL:", "Unregister" }, rows);
+ } else if (host != null && host.length() > 0) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<URL> providers = RegistryContainer.getInstance().getProvidersByHost(host);
+ if (providers != null && providers.size() > 0) {
+ for (URL u : providers) {
+ List<String> row = new ArrayList<String>();
+ String s = u.toFullString();
+ row.add(s.replace("&", "&"));
+ row.add("<button onclick=\"if(confirm('Confirm unregister provider?')){window.location.href='unregister.html?host=" + host + "&provider=" + URL.encode(s) + "';}\">Unregister</button>");
+ rows.add(row);
+ }
+ }
+ return new Page("<a href=\"hosts.html\">Hosts</a> > " + NetUtils.getHostName(host) + "/" + host + " > Providers | <a href=\"consumers.html?host=" + host + "\">Consumers</a>", "Providers (" + rows.size() + ")",
+ new String[] { "Provider URL:", "Unregister" }, rows);
+ } else if (application != null && application.length() > 0) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<URL> providers = RegistryContainer.getInstance().getProvidersByApplication(application);
+ if (providers != null && providers.size() > 0) {
+ for (URL u : providers) {
+ List<String> row = new ArrayList<String>();
+ String s = u.toFullString();
+ row.add(s.replace("&", "&"));
+ row.add("<button onclick=\"if(confirm('Confirm unregister provider?')){window.location.href='unregister.html?application=" + application + "&provider=" + URL.encode(s) + "';}\">Unregister</button>");
+ rows.add(row);
+ }
+ }
+ return new Page("<a href=\"applications.html\">Applications</a> > " + application + " > Providers | <a href=\"consumers.html?application=" + application + "\">Consumers</a> | <a href=\"dependencies.html?application=" + application + "\">Depends On</a> | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>", "Providers (" + rows.size() + ")",
+ new String[] { "Provider URL:", "Unregister" }, rows);
+ } else {
+ throw new IllegalArgumentException("Please input service or host or application parameter.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ServicesPageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ServicesPageHandler.java
new file mode 100644
index 0000000..cc0e589
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/ServicesPageHandler.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Menu;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.simple.RegistryContainer;
+
+/**
+ * ServicesPageHandler
+ *
+ * @author william.liangf
+ */
+@Menu(name = "Services", desc = "Show registered services.", order = 2000)
+public class ServicesPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ Set<String> services = RegistryContainer.getInstance().getServices();
+ List<List<String>> rows = new ArrayList<List<String>>();
+ int providerCount = 0;
+ int consumerCount = 0;
+ if (services != null && services.size() > 0) {
+ for (String service : services) {
+ List<URL> providers = RegistryContainer.getInstance().getProvidersByService(service);
+ int providerSize = providers == null ? 0 : providers.size();
+ providerCount += providerSize;
+ List<URL> consumers = RegistryContainer.getInstance().getConsumersByService(service);
+ int consumerSize = consumers == null ? 0 : consumers.size();
+ consumerCount += consumerSize;
+ List<String> row = new ArrayList<String>();
+ row.add(service);
+ if (providerSize > 0 || consumerSize > 0) {
+ if (providerSize > 0) {
+ URL provider = providers.iterator().next();
+ row.add(provider.getParameter(Constants.APPLICATION_KEY, ""));
+ row.add(provider.getParameter("owner", "") + (provider.hasParameter("organization") ? " (" + provider.getParameter("organization") + ")" : ""));
+ } else {
+ row.add("");
+ row.add("");
+ }
+ row.add(providerSize == 0 ? "<font color=\"red\">No provider</a>" : "<a href=\"providers.html?service=" + service + "\">Providers(" + providerSize + ")</a>");
+ row.add(consumerSize == 0 ? "<font color=\"blue\">No consumer</a>" : "<a href=\"consumers.html?service=" + service + "\">Consumers(" + consumerSize + ")</a>");
+ row.add("<a href=\"statistics.html?service=" + service + "\">Statistics</a>");
+ row.add("<a href=\"charts.html?service=" + service + "\">Charts</a>");
+ rows.add(row);
+ }
+ }
+ }
+ return new Page("Services", "Services (" + rows.size() + ")",
+ new String[] { "Service Name:", "Application", "Owner", "Providers(" + providerCount + ")", "Consumers(" + consumerCount + ")", "Statistics", "Charts" }, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/StatisticsPageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/StatisticsPageHandler.java
new file mode 100644
index 0000000..d235dd5
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/StatisticsPageHandler.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.monitor.simple.CountUtils;
+import com.alibaba.dubbo.monitor.simple.SimpleMonitorService;
+
+/**
+ * StatisticsPageHandler
+ *
+ * @author william.liangf
+ */
+public class StatisticsPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String service = url.getParameter("service");
+ if (service == null || service.length() == 0) {
+ throw new IllegalArgumentException("Please input service parameter.");
+ }
+ String date = url.getParameter("date");
+ if (date == null || date.length() == 0) {
+ date = new SimpleDateFormat("yyyyMMdd").format(new Date());
+ }
+ String expand = url.getParameter("expand");
+ List<List<String>> rows = new ArrayList<List<String>>();
+ String directory = SimpleMonitorService.getInstance().getStatisticsDirectory();
+ String filename = directory + "/" + date + "/" + service;
+ File serviceDir = new File(filename);
+ if (serviceDir.exists()) {
+ File[] methodDirs = serviceDir.listFiles();
+ for (File methodDir : methodDirs) {
+ long[] statistics = newStatistics();
+ Map<String, long[]> expandMap = new HashMap<String, long[]>();
+ File[] consumerDirs = methodDir.listFiles();
+ for (File consumerDir : consumerDirs) {
+ long[] expandStatistics = null;
+ if (MonitorService.CONSUMER.equals(expand)) {
+ expandStatistics = newStatistics();
+ expandMap.put(consumerDir.getName(), expandStatistics);
+ }
+ File[] providerDirs = consumerDir.listFiles();
+ for (File providerDir : providerDirs) {
+ if (MonitorService.PROVIDER.equals(expand)) {
+ expandStatistics = newStatistics();
+ expandMap.put(providerDir.getName(), expandStatistics);
+ }
+ appendStatistics(providerDir, statistics);
+ if (expandStatistics != null) {
+ appendStatistics(providerDir, expandStatistics);
+ }
+ }
+ }
+ rows.add(toRow(methodDir.getName(), statistics));
+ if (expandMap != null && expandMap.size() > 0) {
+ for (Map.Entry<String, long[]> entry : expandMap.entrySet()) {
+ String node = MonitorService.CONSUMER.equals(expand) ? "<--" : "-->";
+ rows.add(toRow(" |" + node + " " + entry.getKey(), entry.getValue()));
+ }
+ }
+ }
+ }
+ StringBuilder nav = new StringBuilder();
+ nav.append("<a href=\"services.html\">Services</a> > ");
+ nav.append(service);
+ nav.append(" > <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> > <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;}\" /> > ");
+ 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]) + " --> " + String.valueOf(statistics[1]));
+ row.add(String.valueOf(statistics[2]) + " --> " + String.valueOf(statistics[3]));
+ row.add(String.valueOf(statistics[0] == 0 ? 0 : statistics[4] / statistics[0])
+ + " --> " + String.valueOf(statistics[1] == 0 ? 0 : statistics[5] / statistics[1]));
+ row.add(String.valueOf(statistics[6]) + " --> " + String.valueOf(statistics[7]));
+ row.add(String.valueOf(statistics[8]) + " --> " + String.valueOf(statistics[9]));
+ return row;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnregisterPageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnregisterPageHandler.java
new file mode 100644
index 0000000..3afc23c
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnregisterPageHandler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.simple.RegistryContainer;
+
+/**
+ * UnregisterPageHandler
+ *
+ * @author william.liangf
+ */
+public class UnregisterPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String provider = url.getParameterAndDecoded("provider");
+ if (provider == null || provider.length() == 0) {
+ throw new IllegalArgumentException("Please input provider parameter.");
+ }
+ URL providerUrl = URL.valueOf(provider);
+ RegistryContainer.getInstance().getRegistry().unregister(providerUrl);
+ String parameter;
+ if (url.hasParameter("service")) {
+ parameter = "service=" + url.getParameter("service");
+ } else if (url.hasParameter("host")) {
+ parameter = "host=" + url.getParameter("host");
+ } else if (url.hasParameter("application")) {
+ parameter = "application=" + url.getParameter("application");
+ } else {
+ parameter = "service=" + providerUrl.getServiceInterface();
+ }
+ return new Page("<script type=\"text/javascript\">window.location.href=\"providers.html?" + parameter + "\";</script>");
+ }
+
+}
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnsubscribePageHandler.java b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnsubscribePageHandler.java
new file mode 100644
index 0000000..456ae47
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/simple/pages/UnsubscribePageHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1999-2012 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.simple.pages;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.monitor.simple.RegistryContainer;
+import com.alibaba.dubbo.registry.NotifyListener;
+
+/**
+ * UnsubscribePageHandler
+ *
+ * @author william.liangf
+ */
+public class UnsubscribePageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ String consumer = url.getParameterAndDecoded("consumer");
+ if (consumer == null || consumer.length() == 0) {
+ throw new IllegalArgumentException("Please input consumer parameter.");
+ }
+ URL consumerUrl = URL.valueOf(consumer);
+ RegistryContainer.getInstance().getRegistry().unsubscribe(consumerUrl, NotifyListenerAdapter.NOTIFY_LISTENER);
+ String parameter;
+ if (url.hasParameter("service")) {
+ parameter = "service=" + url.getParameter("service");
+ } else if (url.hasParameter("host")) {
+ parameter = "host=" + url.getParameter("host");
+ } else if (url.hasParameter("application")) {
+ parameter = "application=" + url.getParameter("application");
+ } else {
+ parameter = "service=" + consumerUrl.getServiceInterface();
+ }
+ return new Page("<script type=\"text/javascript\">window.location.href=\"consumers.html?" + parameter + "\";</script>");
+ }
+
+ private static class NotifyListenerAdapter implements NotifyListener {
+
+ public static final NotifyListener NOTIFY_LISTENER = new NotifyListenerAdapter();
+
+ private NotifyListenerAdapter() {}
+
+ public void notify(List<URL> urls) {}
+
+ }
+
+}
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container b/dubbo-simple/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..139ca06
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.Container
@@ -0,0 +1 @@
+registry=com.alibaba.dubbo.monitor.simple.RegistryContainer
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler b/dubbo-simple/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..23404cc
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,10 @@
+services=com.alibaba.dubbo.monitor.simple.pages.ServicesPageHandler
+providers=com.alibaba.dubbo.monitor.simple.pages.ProvidersPageHandler
+consumers=com.alibaba.dubbo.monitor.simple.pages.ConsumersPageHandler
+statistics=com.alibaba.dubbo.monitor.simple.pages.StatisticsPageHandler
+charts=com.alibaba.dubbo.monitor.simple.pages.ChartsPageHandler
+applications=com.alibaba.dubbo.monitor.simple.pages.ApplicationsPageHandler
+dependencies=com.alibaba.dubbo.monitor.simple.pages.DependenciesPageHandler
+hosts=com.alibaba.dubbo.monitor.simple.pages.HostsPageHandler
+unregister=com.alibaba.dubbo.monitor.simple.pages.UnregisterPageHandler
+unsubscribe=com.alibaba.dubbo.monitor.simple.pages.UnsubscribePageHandler
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/main/resources/META-INF/spring/dubbo-monitor-simple.xml b/dubbo-simple/dubbo-monitor-simple/src/main/resources/META-INF/spring/dubbo-monitor-simple.xml
new file mode 100644
index 0000000..f6bfef2
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitor.java b/dubbo-simple/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitor.java
new file mode 100644
index 0000000..0ed6b31
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorServiceTest.java b/dubbo-simple/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/simple/SimpleMonitorServiceTest.java
new file mode 100644
index 0000000..04d0c12
--- /dev/null
+++ b/dubbo-simple/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().collect(new URL("dubbo", NetUtils.getLocalHost(), 0));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/test/resources/dubbo.properties b/dubbo-simple/dubbo-monitor-simple/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..8ee345a
--- /dev/null
+++ b/dubbo-simple/dubbo-monitor-simple/src/test/resources/dubbo.properties
@@ -0,0 +1,29 @@
+##
+# Copyright 1999-2011 Alibaba Group.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+dubbo.container=log4j,spring,registry,jetty
+dubbo.application.name=simple-monitor
+dubbo.application.owner=
+dubbo.registry.address=multicast://224.5.6.7:1234
+#dubbo.registry.address=zookeeper://127.0.0.1:2181
+#dubbo.registry.address=redis://127.0.0.1:6379
+#dubbo.registry.address=dubbo://127.0.0.1:9090
+dubbo.protocol.port=7070
+dubbo.jetty.port=8080
+dubbo.jetty.directory=${user.home}/monitor
+dubbo.charts.directory=${dubbo.jetty.directory}/charts
+dubbo.statistics.directory=${user.home}/monitor/statistics
+#dubbo.log4j.file=logs/dubbo-demo-consumer.log
+#dubbo.log4j.level=WARN
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-monitor-simple/src/test/resources/log4j.xml b/dubbo-simple/dubbo-monitor-simple/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-registry-simple/pom.xml b/dubbo-simple/dubbo-registry-simple/pom.xml
new file mode 100644
index 0000000..beeccc9
--- /dev/null
+++ b/dubbo-simple/dubbo-registry-simple/pom.xml
@@ -0,0 +1,84 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-simple</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-registry-simple</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The simple registry module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack</id>
+ <phase>package</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo</artifactId>
+ <version>${project.parent.version}</version>
+ <outputDirectory>${project.build.directory}/dubbo</outputDirectory>
+ <includes>META-INF/assembly/**</includes>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptor>src/main/assembly/assembly.xml</descriptor>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/dubbo-simple/dubbo-registry-simple/src/main/assembly/assembly.xml b/dubbo-simple/dubbo-registry-simple/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..25235d9
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-registry-simple/src/main/assembly/conf/dubbo.properties b/dubbo-simple/dubbo-registry-simple/src/main/assembly/conf/dubbo.properties
new file mode 100644
index 0000000..6e6c63e
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/simple/SimpleRegistryService.java b/dubbo-simple/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/simple/SimpleRegistryService.java
new file mode 100644
index 0000000..5fbfc34
--- /dev/null
+++ b/dubbo-simple/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/simple/SimpleRegistryService.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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);
+ }
+ }
+ String client = RpcContext.getContext().getRemoteAddressString();
+ ConcurrentMap<String, Set<NotifyListener>> clientListeners = remoteSubscribed.get(client);
+ if (clientListeners == null) {
+ remoteSubscribed.putIfAbsent(client, new ConcurrentHashMap<String, Set<NotifyListener>>());
+ clientListeners = remoteSubscribed.get(client);
+ }
+ String key = url.toFullString();
+ Set<NotifyListener> listeners = clientListeners.get(key);
+ if (listeners == null) {
+ clientListeners.putIfAbsent(key, new ConcurrentHashSet<NotifyListener>());
+ listeners = clientListeners.get(key);
+ }
+ listeners.add(listener);
+ super.subscribe(url, listener);
+ subscribed(url, listener);
+ }
+
+ public void unsubscribe(URL url, NotifyListener listener) {
+ if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
+ && url.getParameter(Constants.REGISTER_KEY, true)) {
+ unregister(url);
+ }
+ 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<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
+ URL key = entry.getKey();
+ if (UrlUtils.isMatch(key, url)) {
+ List<URL> list = lookup(key);
+ for (NotifyListener listener : entry.getValue()) {
+ notify(key, listener, list);
+ }
+ }
+ }
+ }
+
+ protected void unregistered(URL url) {
+ for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
+ URL key = entry.getKey();
+ if (UrlUtils.isMatch(key, url)) {
+ List<URL> list = lookup(key);
+ for (NotifyListener listener : entry.getValue()) {
+ notify(key, listener, list);
+ }
+ }
+ }
+ }
+
+ protected void subscribed(URL url, NotifyListener listener) {
+ List<URL> urls = lookup(url);
+ notify(url, listener, urls);
+ }
+
+ public void doRegister(URL url) {
+ }
+
+ public void doUnregister(URL url) {
+ }
+
+ public void doSubscribe(URL url, NotifyListener listener) {
+ }
+
+ public void doUnsubscribe(URL url, NotifyListener listener) {
+ }
+
+ public void disconnect() {
+ String client = RpcContext.getContext().getRemoteAddressString();
+ if (logger.isInfoEnabled()) {
+ logger.info("Disconnected " + client);
+ }
+ Set<String> urls = remoteRegistered.get(client);
+ if (urls != null && urls.size() > 0) {
+ for (String url : urls) {
+ unregister(URL.valueOf(url));
+ }
+ }
+ 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-simple/dubbo-registry-simple/src/main/resources/META-INF/spring/dubbo-registry-simple.xml b/dubbo-simple/dubbo-registry-simple/src/main/resources/META-INF/spring/dubbo-registry-simple.xml
new file mode 100644
index 0000000..561adb3
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistry.java b/dubbo-simple/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistry.java
new file mode 100644
index 0000000..efa518a
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistryServiceTest.java b/dubbo-simple/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/simple/SimpleRegistryServiceTest.java
new file mode 100644
index 0000000..76e19b7
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-registry-simple/src/test/resources/dubbo.properties b/dubbo-simple/dubbo-registry-simple/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..246fc36
--- /dev/null
+++ b/dubbo-simple/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-simple/dubbo-registry-simple/src/test/resources/log4j.xml b/dubbo-simple/dubbo-registry-simple/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-simple/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-simple/pom.xml b/dubbo-simple/pom.xml
new file mode 100644
index 0000000..31759e8
--- /dev/null
+++ b/dubbo-simple/pom.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.
+-->
+<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.2.2</version>
+ </parent>
+ <artifactId>dubbo-simple</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The simple implementation module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-registry-simple</module>
+ <module>dubbo-monitor-simple</module>
+ </modules>
+</project>
diff --git a/dubbo-test/dubbo-test-benchmark/pom.xml b/dubbo-test/dubbo-test-benchmark/pom.xml
new file mode 100644
index 0000000..d24462b
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/pom.xml
@@ -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.
+-->
+<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-test</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-test-benchmark</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The performance benchmark kit test module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+ <execution>
+ <id>assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>${basedir}/src/assembly/release.xml</descriptor>
+ </descriptors>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/assembly/dev.xml b/dubbo-test/dubbo-test-benchmark/src/assembly/dev.xml
new file mode 100644
index 0000000..77bdf21
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/assembly/dev.xml
@@ -0,0 +1,19 @@
+<assembly>
+ <id>dist</id>
+ <formats>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <fileSets>
+ <fileSet>
+ <directory>src/main/resources</directory>
+ <outputDirectory>dubbo.benchmark</outputDirectory>
+ <fileMode>0755</fileMode>
+ </fileSet>
+ </fileSets>
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>dubbo.benchmark/lib</outputDirectory>
+ </dependencySet>
+ </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/assembly/release.xml b/dubbo-test/dubbo-test-benchmark/src/assembly/release.xml
new file mode 100644
index 0000000..77bdf21
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/assembly/release.xml
@@ -0,0 +1,19 @@
+<assembly>
+ <id>dist</id>
+ <formats>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <fileSets>
+ <fileSet>
+ <directory>src/main/resources</directory>
+ <outputDirectory>dubbo.benchmark</outputDirectory>
+ <fileMode>0755</fileMode>
+ </fileSet>
+ </fileSets>
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>dubbo.benchmark/lib</outputDirectory>
+ </dependencySet>
+ </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/AbstractBenchmarkClient.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/AbstractBenchmarkClient.java
new file mode 100644
index 0000000..6ac6d95
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/AbstractBenchmarkClient.java
@@ -0,0 +1,247 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+/**
+ * nfs-rpc
+ * Apache License
+ *
+ * http://code.google.com/p/nfs-rpc (c) 2011
+ */
+import java.io.BufferedWriter;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+
+/**
+ * Abstract benchmark client,test for difference scenes Usage: -Dwrite.statistics=false BenchmarkClient serverIP
+ * serverPort concurrents timeout codectype requestSize runtime(seconds) clientNums
+ *
+ * @author <a href="mailto:bluedavy@gmail.com">bluedavy</a>
+ */
+public abstract class AbstractBenchmarkClient {
+
+ private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ private static long maxTPS = 0;
+
+ private static long minTPS = 0;
+
+ private static long allRequestSum;
+
+ private static long allResponseTimeSum;
+
+ private static long allErrorRequestSum;
+
+ private static long allErrorResponseTimeSum;
+
+ private static int runtime;
+
+ // < 0
+ private static long below0sum;
+
+ // (0,1]
+ private static long above0sum;
+
+ // (1,5]
+ private static long above1sum;
+
+ // (5,10]
+ private static long above5sum;
+
+ // (10,50]
+ private static long above10sum;
+
+ // (50,100]
+ private static long above50sum;
+
+ // (100,500]
+ private static long above100sum;
+
+ // (500,1000]
+ private static long above500sum;
+
+ // > 1000
+ private static long above1000sum;
+
+ Properties properties = ConfigUtils.getProperties();
+
+ public void run(String[] args) throws Exception {
+
+ final String serverIP = properties.getProperty("serverip");
+ final int serverPort = Integer.parseInt(properties.getProperty("serverport"));
+ final int concurrents = Integer.parseInt(properties.getProperty("concurrents"));
+ final int timeout = Integer.parseInt(properties.getProperty("timeout"));
+ runtime = Integer.parseInt(properties.getProperty("runtime"));
+ final long endtime = System.nanoTime() / 1000L + runtime * 1000 * 1000L;
+ final int clientNums = Integer.parseInt(properties.getProperty("connectionnums"));
+
+ // Print start info
+ Date currentDate = new Date();
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(currentDate);
+ calendar.add(Calendar.SECOND, runtime);
+ StringBuilder startInfo = new StringBuilder(dateFormat.format(currentDate));
+ startInfo.append(" ready to start client benchmark,server is ");
+ startInfo.append(serverIP).append(":").append(serverPort);
+ startInfo.append(",concurrents is: ").append(concurrents);
+ startInfo.append(",clientNums is: ").append(clientNums);
+ startInfo.append(",timeout is:").append(timeout);
+ startInfo.append(" s,the benchmark will end at:").append(dateFormat.format(calendar.getTime()));
+ System.out.println(startInfo.toString());
+
+ CyclicBarrier barrier = new CyclicBarrier(concurrents);
+ CountDownLatch latch = new CountDownLatch(concurrents);
+ List<ClientRunnable> runnables = new ArrayList<ClientRunnable>();
+ // benchmark start after thirty seconds,let java app warm up
+ long beginTime = System.nanoTime() / 1000L + 30 * 1000 * 1000L;
+ for (int i = 0; i < concurrents; i++) {
+ ClientRunnable runnable = getClientRunnable(serverIP, serverPort, clientNums, timeout, barrier, latch,
+ beginTime, endtime);
+ runnables.add(runnable);
+ }
+
+ startRunnables(runnables);
+
+ latch.await();
+
+ // read results & add all
+ // key: runtime second range value: Long[2] array Long[0]: execute count Long[1]: response time sum
+ Map<String, Long[]> times = new HashMap<String, Long[]>();
+ Map<String, Long[]> errorTimes = new HashMap<String, Long[]>();
+ for (ClientRunnable runnable : runnables) {
+ List<long[]> results = runnable.getResults();
+ long[] responseSpreads = results.get(0);
+ below0sum += responseSpreads[0];
+ above0sum += responseSpreads[1];
+ above1sum += responseSpreads[2];
+ above5sum += responseSpreads[3];
+ above10sum += responseSpreads[4];
+ above50sum += responseSpreads[5];
+ above100sum += responseSpreads[6];
+ above500sum += responseSpreads[7];
+ above1000sum += responseSpreads[8];
+ long[] tps = results.get(1);
+ long[] responseTimes = results.get(2);
+ long[] errorTPS = results.get(3);
+ long[] errorResponseTimes = results.get(4);
+ for (int i = 0; i < tps.length; i++) {
+ String key = String.valueOf(i);
+ if (times.containsKey(key)) {
+ Long[] successInfos = times.get(key);
+ Long[] errorInfos = errorTimes.get(key);
+ successInfos[0] += tps[i];
+ successInfos[1] += responseTimes[i];
+ errorInfos[0] += errorTPS[i];
+ errorInfos[1] += errorResponseTimes[i];
+ times.put(key, successInfos);
+ errorTimes.put(key, errorInfos);
+ } else {
+ Long[] successInfos = new Long[2];
+ successInfos[0] = tps[i];
+ successInfos[1] = responseTimes[i];
+ Long[] errorInfos = new Long[2];
+ errorInfos[0] = errorTPS[i];
+ errorInfos[1] = errorResponseTimes[i];
+ times.put(key, successInfos);
+ errorTimes.put(key, errorInfos);
+ }
+ }
+ }
+
+ long ignoreRequest = 0;
+ long ignoreErrorRequest = 0;
+ int maxTimeRange = runtime - 30;
+ // ignore the last 10 second requests,so tps can count more accurate
+ for (int i = 0; i < 10; i++) {
+ Long[] values = times.remove(String.valueOf(maxTimeRange - i));
+ if (values != null) {
+ ignoreRequest += values[0];
+ }
+ Long[] errorValues = errorTimes.remove(String.valueOf(maxTimeRange - i));
+ if (errorValues != null) {
+ ignoreErrorRequest += errorValues[0];
+ }
+ }
+
+ for (Map.Entry<String, Long[]> entry : times.entrySet()) {
+ long successRequest = entry.getValue()[0];
+ long errorRequest = 0;
+ if (errorTimes.containsKey(entry.getKey())) {
+ errorRequest = errorTimes.get(entry.getKey())[0];
+ }
+ allRequestSum += successRequest;
+ allResponseTimeSum += entry.getValue()[1];
+ allErrorRequestSum += errorRequest;
+ if (errorTimes.containsKey(entry.getKey())) {
+ allErrorResponseTimeSum += errorTimes.get(entry.getKey())[1];
+ }
+ long currentRequest = successRequest + errorRequest;
+ if (currentRequest > maxTPS) {
+ maxTPS = currentRequest;
+ }
+ if (minTPS == 0 || currentRequest < minTPS) {
+ minTPS = currentRequest;
+ }
+ }
+
+ boolean isWriteResult = Boolean.parseBoolean(System.getProperty("write.statistics", "false"));
+ if (isWriteResult) {
+ BufferedWriter writer = new BufferedWriter(new FileWriter("benchmark.all.results"));
+ for (Map.Entry<String, Long[]> entry : times.entrySet()) {
+ writer.write(entry.getKey() + "," + entry.getValue()[0] + "," + entry.getValue()[1] + "\r\n");
+ }
+ writer.close();
+ }
+
+ System.out.println("----------Benchmark Statistics--------------");
+ System.out.println(" Concurrents: " + concurrents);
+ System.out.println(" ClientNums: " + clientNums);
+ System.out.println(" Runtime: " + runtime + " seconds");
+ System.out.println(" Benchmark Time: " + times.keySet().size());
+ long benchmarkRequest = allRequestSum + allErrorRequestSum;
+ long allRequest = benchmarkRequest + ignoreRequest + ignoreErrorRequest;
+ System.out.println(" Requests: " + allRequest + " Success: " + (allRequestSum + ignoreRequest) * 100
+ / allRequest + "% (" + (allRequestSum + ignoreRequest) + ") Error: "
+ + (allErrorRequestSum + ignoreErrorRequest) * 100 / allRequest + "% ("
+ + (allErrorRequestSum + ignoreErrorRequest) + ")");
+ System.out.println(" Avg TPS: " + benchmarkRequest / times.keySet().size() + " Max TPS: " + maxTPS
+ + " Min TPS: " + minTPS);
+ System.out.println(" Avg RT: " + (allErrorResponseTimeSum + allResponseTimeSum) / benchmarkRequest / 1000f
+ + "ms");
+ System.out.println(" RT <= 0: " + (below0sum * 100 / allRequest) + "% " + below0sum + "/" + allRequest);
+ System.out.println(" RT (0,1]: " + (above0sum * 100 / allRequest) + "% " + above0sum + "/" + allRequest);
+ System.out.println(" RT (1,5]: " + (above1sum * 100 / allRequest) + "% " + above1sum + "/" + allRequest);
+ System.out.println(" RT (5,10]: " + (above5sum * 100 / allRequest) + "% " + above5sum + "/" + allRequest);
+ System.out.println(" RT (10,50]: " + (above10sum * 100 / allRequest) + "% " + above10sum + "/" + allRequest);
+ System.out.println(" RT (50,100]: " + (above50sum * 100 / allRequest) + "% " + above50sum + "/" + allRequest);
+ System.out.println(" RT (100,500]: " + (above100sum * 100 / allRequest) + "% " + above100sum + "/" + allRequest);
+ System.out.println(" RT (500,1000]: " + (above500sum * 100 / allRequest) + "% " + above500sum + "/"
+ + allRequest);
+ System.out.println(" RT > 1000: " + (above1000sum * 100 / allRequest) + "% " + above1000sum + "/" + allRequest);
+ System.exit(0);
+ }
+
+ public abstract ClientRunnable getClientRunnable(String targetIP, int targetPort, int clientNums, int rpcTimeout,
+ CyclicBarrier barrier, CountDownLatch latch, long startTime,
+ long endTime) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException;
+
+ protected void startRunnables(List<ClientRunnable> runnables) {
+ for (int i = 0; i < runnables.size(); i++) {
+ final ClientRunnable runnable = runnables.get(i);
+ Thread thread = new Thread(runnable, "benchmarkclient-" + i);
+ thread.start();
+ }
+ }
+
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/AbstractBenchmarkServer.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/AbstractBenchmarkServer.java
new file mode 100644
index 0000000..3fade03
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/AbstractBenchmarkServer.java
@@ -0,0 +1,52 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+/**
+ * nfs-rpc Apache License http://code.google.com/p/nfs-rpc (c) 2011
+ */
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+
+/**
+ * Abstract benchmark server Usage: BenchmarkServer listenPort maxThreads responseSize
+ *
+ * @author <a href="mailto:bluedavy@gmail.com">bluedavy</a>
+ */
+public abstract class AbstractBenchmarkServer {
+
+ private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ public void run(String[] args) throws Exception {
+ if (args == null || args.length != 5) {
+ throw new IllegalArgumentException(
+ "must give three args: listenPort | maxThreads | responseSize | transporter | serialization");
+ }
+ int listenPort = Integer.parseInt(args[0]);
+ int maxThreads = Integer.parseInt(args[1]);
+ final int responseSize = Integer.parseInt(args[2]);
+ String transporter = args[3];
+ String serialization = args[4];
+ System.out.println(dateFormat.format(new Date()) + " ready to start server,listenPort is: " + listenPort
+ + ",maxThreads is:" + maxThreads + ",responseSize is:" + responseSize
+ + " bytes,transporter is:" + transporter + ",serialization is:" + serialization);
+ StringBuilder url = new StringBuilder();
+ url.append("exchange://0.0.0.0:");
+ url.append(listenPort);
+ url.append("?transporter=");
+ url.append(transporter);
+ url.append("&serialization=");
+ url.append(serialization);
+ url.append("&threads=");
+ url.append(maxThreads);
+ Exchangers.bind(url.toString(), new ExchangeHandlerAdapter() {
+
+ public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
+ return new ResponseObject(responseSize); // 发送响应
+ }
+ });
+ }
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/AbstractClientRunnable.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/AbstractClientRunnable.java
new file mode 100644
index 0000000..725ae9b
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/AbstractClientRunnable.java
@@ -0,0 +1,173 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+/**
+ * nfs-rpc Apache License http://code.google.com/p/nfs-rpc (c) 2011
+ */
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Simple Processor RPC Benchmark Client Thread
+ *
+ * @author <a href="mailto:bluedavy@gmail.com">bluedavy</a>
+ */
+public abstract class AbstractClientRunnable implements ClientRunnable {
+
+ private static final Log LOGGER = LogFactory.getLog(AbstractClientRunnable.class);
+
+ private CyclicBarrier barrier;
+
+ private CountDownLatch latch;
+
+ private long endTime;
+
+ private boolean running = true;
+
+ // response time spread
+ private long[] responseSpreads = new long[9];
+
+ // error request per second
+ private long[] errorTPS = null;
+
+ // error response times per second
+ private long[] errorResponseTimes = null;
+
+ // tps per second
+ private long[] tps = null;
+
+ // response times per second
+ private long[] responseTimes = null;
+
+ // benchmark startTime
+ private long startTime;
+
+ // benchmark maxRange
+ private int maxRange;
+
+ private ServiceFactory serviceFactory = new ServiceFactory();
+
+ public AbstractClientRunnable(String targetIP, int targetPort, int clientNums, int rpcTimeout,
+ CyclicBarrier barrier, CountDownLatch latch, long startTime, long endTime){
+
+ this.barrier = barrier;
+ this.latch = latch;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ serviceFactory.setTargetIP(targetIP);
+ serviceFactory.setClientNums(clientNums);
+ serviceFactory.setTargetPort(targetPort);
+ serviceFactory.setConnectTimeout(rpcTimeout);
+ maxRange = (Integer.parseInt(String.valueOf((endTime - startTime))) / 1000000) + 1;
+ errorTPS = new long[maxRange];
+ errorResponseTimes = new long[maxRange];
+ tps = new long[maxRange];
+ responseTimes = new long[maxRange];
+ // init
+ for (int i = 0; i < maxRange; i++) {
+ errorTPS[i] = 0;
+ errorResponseTimes[i] = 0;
+ tps[i] = 0;
+ responseTimes[i] = 0;
+ }
+ }
+
+ public void run() {
+ try {
+ barrier.await();
+ } catch (Exception e) {
+ // IGNORE
+ }
+ runJavaAndHessian();
+ latch.countDown();
+ }
+
+ private void runJavaAndHessian() {
+ while (running) {
+ long beginTime = System.nanoTime() / 1000L;
+ if (beginTime >= endTime) {
+ running = false;
+ break;
+ }
+ try {
+ Object result = invoke(serviceFactory);
+ long currentTime = System.nanoTime() / 1000L;
+ if (beginTime <= startTime) {
+ continue;
+ }
+ long consumeTime = currentTime - beginTime;
+ sumResponseTimeSpread(consumeTime);
+ int range = Integer.parseInt(String.valueOf(beginTime - startTime)) / 1000000;
+ if (range >= maxRange) {
+ System.err.println("benchmark range exceeds maxRange,range is: " + range + ",maxRange is: "
+ + maxRange);
+ continue;
+ }
+ if (result != null) {
+ tps[range] = tps[range] + 1;
+ responseTimes[range] = responseTimes[range] + consumeTime;
+ } else {
+ LOGGER.error("server return result is null");
+ errorTPS[range] = errorTPS[range] + 1;
+ errorResponseTimes[range] = errorResponseTimes[range] + consumeTime;
+ }
+ } catch (Exception e) {
+ LOGGER.error("client.invokeSync error", e);
+ long currentTime = System.nanoTime() / 1000L;
+ if (beginTime <= startTime) {
+ continue;
+ }
+ long consumeTime = currentTime - beginTime;
+ sumResponseTimeSpread(consumeTime);
+ int range = Integer.parseInt(String.valueOf(beginTime - startTime)) / 1000000;
+ if (range >= maxRange) {
+ System.err.println("benchmark range exceeds maxRange,range is: " + range + ",maxRange is: "
+ + maxRange);
+ continue;
+ }
+ errorTPS[range] = errorTPS[range] + 1;
+ errorResponseTimes[range] = errorResponseTimes[range] + consumeTime;
+ }
+ }
+ }
+
+ public abstract Object invoke(ServiceFactory<?> serviceFactory);
+
+ public List<long[]> getResults() {
+ List<long[]> results = new ArrayList<long[]>();
+ results.add(responseSpreads);
+ results.add(tps);
+ results.add(responseTimes);
+ results.add(errorTPS);
+ results.add(errorResponseTimes);
+ return results;
+ }
+
+ private void sumResponseTimeSpread(long responseTime) {
+ responseTime = responseTime / 1000L;
+ if (responseTime <= 0) {
+ responseSpreads[0] = responseSpreads[0] + 1;
+ } else if (responseTime > 0 && responseTime <= 1) {
+ responseSpreads[1] = responseSpreads[1] + 1;
+ } else if (responseTime > 1 && responseTime <= 5) {
+ responseSpreads[2] = responseSpreads[2] + 1;
+ } else if (responseTime > 5 && responseTime <= 10) {
+ responseSpreads[3] = responseSpreads[3] + 1;
+ } else if (responseTime > 10 && responseTime <= 50) {
+ responseSpreads[4] = responseSpreads[4] + 1;
+ } else if (responseTime > 50 && responseTime <= 100) {
+ responseSpreads[5] = responseSpreads[5] + 1;
+ } else if (responseTime > 100 && responseTime <= 500) {
+ responseSpreads[6] = responseSpreads[6] + 1;
+ } else if (responseTime > 500 && responseTime <= 1000) {
+ responseSpreads[7] = responseSpreads[7] + 1;
+ } else if (responseTime > 1000) {
+ responseSpreads[8] = responseSpreads[8] + 1;
+ }
+ }
+
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/BenchmarkClient.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/BenchmarkClient.java
new file mode 100644
index 0000000..68102e3
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/BenchmarkClient.java
@@ -0,0 +1,19 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+public class BenchmarkClient extends AbstractBenchmarkClient {
+
+ @Override
+ public ClientRunnable getClientRunnable(String targetIP, int targetPort, int clientNums, int rpcTimeout,
+ CyclicBarrier barrier,
+ CountDownLatch latch, long endTime, long startTime) {
+ return new SimpleProcessorBenchmarkClientRunnable(targetIP, targetPort, clientNums, rpcTimeout,
+ barrier, latch, startTime, endTime);
+ }
+
+ public static void main(String[] args) throws Exception {
+ new BenchmarkClient().run(args);
+ }
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/BenchmarkServer.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/BenchmarkServer.java
new file mode 100644
index 0000000..cf974b5
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/BenchmarkServer.java
@@ -0,0 +1,11 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+public class BenchmarkServer extends AbstractBenchmarkServer {
+
+ public static void main(String[] args) throws Exception {
+ new BenchmarkServer().run(args);
+ synchronized (BenchmarkServer.class) {
+ BenchmarkServer.class.wait();
+ }
+ }
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ClientRunnable.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ClientRunnable.java
new file mode 100644
index 0000000..ba48cfc
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ClientRunnable.java
@@ -0,0 +1,20 @@
+/**
+ * nfs-rpc
+ * Apache License
+ *
+ * http://code.google.com/p/nfs-rpc (c) 2011
+ */
+package com.alibaba.dubbo.rpc.benchmark;
+
+import java.util.List;
+
+/**
+ * client runnable,so we can collect results
+ *
+ * @author <a href="mailto:bluedavy@gmail.com">bluedavy</a>
+ */
+public interface ClientRunnable extends Runnable {
+
+ public List<long[]> getResults();
+
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/DemoService.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/DemoService.java
new file mode 100644
index 0000000..4c47723
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/DemoService.java
@@ -0,0 +1,11 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+
+/**
+ * TODO Comment of HelloService
+ *
+ * @author tony.chenl
+ */
+public interface DemoService {
+ public Object sendRequest(Object requestObject);
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/DemoServiceImpl.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/DemoServiceImpl.java
new file mode 100644
index 0000000..2ed2bc5
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/DemoServiceImpl.java
@@ -0,0 +1,15 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+
+/**
+ * TODO Comment of HelloService
+ *
+ * @author tony.chenl
+ */
+public class DemoServiceImpl implements DemoService{
+ ResponseObject responseObject = new ResponseObject(100);
+
+ public Object sendRequest(Object request) {
+ return responseObject;
+ }
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ExchangeClientFactory.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ExchangeClientFactory.java
new file mode 100644
index 0000000..81af8bb
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ExchangeClientFactory.java
@@ -0,0 +1,101 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+/**
+ * nfs-rpc Apache License http://code.google.com/p/nfs-rpc (c) 2011
+ */
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.FutureTask;
+
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+
+/**
+ * Abstract ExchangeClient Factory,create custom nums ExchangeClient
+ *
+ * @author <a href="mailto:bluedavy@gmail.com">bluedavy</a>
+ */
+public class ExchangeClientFactory {
+
+ // Cache ExchangeClient
+ private static ConcurrentHashMap<String, FutureTask<List<ExchangeClient>>> clients = new ConcurrentHashMap<String, FutureTask<List<ExchangeClient>>>();
+
+ public ExchangeClient get(final String targetIP, final int targetPort, final int connectTimeout) throws Exception {
+ return get(targetIP, targetPort, connectTimeout, 1);
+ }
+
+ public ExchangeClient get(final String targetIP, final int targetPort, final int connectTimeout,
+ final int clientNums) throws Exception {
+ String key = targetIP + ":" + targetPort;
+ if (clients.containsKey(key)) {
+ if (clientNums == 1) {
+ return clients.get(key).get().get(0);
+ } else {
+ Random random = new Random();
+ return clients.get(key).get().get(random.nextInt(clientNums));
+ }
+ } else {
+ FutureTask<List<ExchangeClient>> task = new FutureTask<List<ExchangeClient>>(
+ new Callable<List<ExchangeClient>>() {
+
+ public List<ExchangeClient> call()
+ throws Exception {
+ List<ExchangeClient> clients = new ArrayList<ExchangeClient>(
+ clientNums);
+ for (int i = 0; i < clientNums; i++) {
+ clients.add(createClient(targetIP,
+ targetPort,
+ connectTimeout));
+ }
+ return clients;
+ }
+ });
+ FutureTask<List<ExchangeClient>> currentTask = clients.putIfAbsent(key, task);
+ if (currentTask == null) {
+ task.run();
+ } else {
+ task = currentTask;
+ }
+ if (clientNums == 1) return task.get().get(0);
+ else {
+ Random random = new Random();
+ return task.get().get(random.nextInt(clientNums));
+ }
+ }
+ }
+
+ public void removeClient(String key, ExchangeClient ExchangeClient) {
+ try {
+ // TODO: Fix It
+ clients.remove(key);
+ // clients.get(key).get().remove(ExchangeClient);
+ // clients.get(key)
+ // .get()
+ // .add(createClient(ExchangeClient.getServerIP(),
+ // ExchangeClient.getServerPort(), ExchangeClient.getConnectTimeout(),
+ // key));
+ } catch (Exception e) {
+ // IGNORE
+ }
+ }
+
+ public static ExchangeClientFactory getInstance() {
+ throw new UnsupportedOperationException("should be implemented by true class");
+ }
+
+ protected ExchangeClient createClient(String targetIP, int targetPort, int connectTimeout) throws Exception {
+ StringBuilder url = new StringBuilder();
+ url.append("exchange://");
+ url.append(targetIP);
+ url.append(":");
+ url.append(targetPort);
+ url.append("?");
+ url.append("timeout=");
+ url.append(connectTimeout);
+ return Exchangers.connect(url.toString());
+ }
+
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/RequestObject.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/RequestObject.java
new file mode 100644
index 0000000..c7c498e
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/RequestObject.java
@@ -0,0 +1,37 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+/**
+ * nfs-rpc
+ * Apache License
+ *
+ * http://code.google.com/p/nfs-rpc (c) 2011
+ */
+import java.io.Serializable;
+
+/**
+ * Just for RPC Benchmark Test,request object
+ *
+ * @author <a href="mailto:bluedavy@gmail.com">bluedavy</a>
+ */
+public class RequestObject implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public RequestObject(){
+ }
+
+ private byte[] bytes = null;
+
+ public void setBytes(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ public RequestObject(int size){
+ bytes = new byte[size];
+ }
+
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ResponseObject.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ResponseObject.java
new file mode 100644
index 0000000..c8ede56
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ResponseObject.java
@@ -0,0 +1,29 @@
+package com.alibaba.dubbo.rpc.benchmark;
+/**
+ * nfs-rpc
+ * Apache License
+ *
+ * http://code.google.com/p/nfs-rpc (c) 2011
+ */
+import java.io.Serializable;
+/**
+ * Just for RPC Benchmark Test,response object
+ *
+ * @author <a href="mailto:bluedavy@gmail.com">bluedavy</a>
+ */
+public class ResponseObject implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private byte[] bytes = null;
+
+ public ResponseObject(int size){
+ bytes = new byte[size];
+ }
+
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/RpcBenchmarkClient.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/RpcBenchmarkClient.java
new file mode 100644
index 0000000..4dcb35e
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/RpcBenchmarkClient.java
@@ -0,0 +1,24 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+public class RpcBenchmarkClient extends AbstractBenchmarkClient {
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public ClientRunnable getClientRunnable(String targetIP, int targetPort, int clientNums, int rpcTimeout,
+ CyclicBarrier barrier, CountDownLatch latch, long startTime, long endTime) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
+ String runnable = properties.getProperty("classname");
+ Class[] parameterTypes = new Class[] { String.class, int.class, int.class, int.class, CyclicBarrier.class,
+ CountDownLatch.class, long.class, long.class };
+ Object[] parameters = new Object[] { targetIP, targetPort, clientNums, rpcTimeout, barrier, latch, startTime,
+ endTime };
+ return (ClientRunnable) Class.forName(runnable).getConstructor(parameterTypes).newInstance(parameters);
+ }
+
+ public static void main(String[] args) throws Exception {
+ new RpcBenchmarkClient().run(args);
+ }
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/RpcBenchmarkServer.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/RpcBenchmarkServer.java
new file mode 100644
index 0000000..cfe846b
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/RpcBenchmarkServer.java
@@ -0,0 +1,18 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class RpcBenchmarkServer extends AbstractBenchmarkServer {
+
+ public static void main(String[] args) throws Exception {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("ProviderSample.xml");
+ ctx.start();
+ synchronized (RpcBenchmarkServer.class) {
+ try {
+ RpcBenchmarkServer.class.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ServiceFactory.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ServiceFactory.java
new file mode 100644
index 0000000..b699b0e
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/ServiceFactory.java
@@ -0,0 +1,91 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+
+/**
+ * Abstract Service Factory,create custom nums Service
+ *
+ * @author tony.chenl
+ */
+public class ServiceFactory<T> {
+
+ String targetIP = null;
+
+ int targetPort = 0;
+
+ int connectTimeout = 0;
+
+ int clientNums = 0;
+
+ public String getTargetIP() {
+ return targetIP;
+ }
+
+ public void setTargetIP(String targetIP) {
+ this.targetIP = targetIP;
+ }
+
+ public int getTargetPort() {
+ return targetPort;
+ }
+
+ public void setTargetPort(int targetPort) {
+ this.targetPort = targetPort;
+ }
+
+ public int getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ public void setConnectTimeout(int connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ }
+
+ public int getClientNums() {
+ return clientNums;
+ }
+
+ public void setClientNums(int clientNums) {
+ this.clientNums = clientNums;
+ }
+
+ // Cache ExchangeClient
+ private static ConcurrentHashMap<String, Object> services = new ConcurrentHashMap<String, Object>();
+
+ @SuppressWarnings("unchecked")
+ public T get(final Class<T> cls){
+ String key = cls.getName();
+ if (services.containsKey(key)) {
+ return (T) services.get(key);
+
+ } else {
+ T service = createClient(cls, targetIP, targetPort, connectTimeout,clientNums);
+ services.put(key, service);
+ return (T) services.get(key);
+ }
+ }
+
+ protected T createClient(Class<T> cls, String targetIP, int targetPort, int connectTimeout,int clientNums){
+ ReferenceConfig<T> referenceConfig = new ReferenceConfig<T>();
+ referenceConfig.setInterface(cls);
+ StringBuilder url = new StringBuilder();
+ url.append("dubbo://");
+ url.append(targetIP);
+ url.append(":");
+ url.append(targetPort);
+ url.append("/");
+ url.append(cls.getName());
+ referenceConfig.setUrl(url.toString());
+ // hardcode
+ referenceConfig.setConnections(clientNums);
+ ApplicationConfig application = new ApplicationConfig();
+ application.setName("dubbo_consumer");
+ referenceConfig.setApplication(application);
+ referenceConfig.setTimeout(connectTimeout);
+ return referenceConfig.get();
+ }
+
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/SimpleProcessorBenchmarkClientRunnable.java b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/SimpleProcessorBenchmarkClientRunnable.java
new file mode 100644
index 0000000..f663a6a
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/java/com/alibaba/dubbo/rpc/benchmark/SimpleProcessorBenchmarkClientRunnable.java
@@ -0,0 +1,184 @@
+package com.alibaba.dubbo.rpc.benchmark;
+
+/**
+ * nfs-rpc Apache License http://code.google.com/p/nfs-rpc (c) 2011
+ */
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Simple Processor RPC Benchmark Client Thread
+ *
+ * @author <a href="mailto:bluedavy@gmail.com">bluedavy</a>
+ */
+public class SimpleProcessorBenchmarkClientRunnable implements ClientRunnable {
+
+ private static final Log LOGGER = LogFactory.getLog(SimpleProcessorBenchmarkClientRunnable.class);
+
+ private int requestSize;
+
+ private CyclicBarrier barrier;
+
+ private CountDownLatch latch;
+
+ private long endTime;
+
+ private boolean running = true;
+
+ private ExchangeClientFactory clientFactory = new ExchangeClientFactory();
+
+ private String targetIP;
+
+ private int targetPort;
+
+ private int clientNums;
+
+ private int rpcTimeout;
+
+ // response time spread
+ private long[] responseSpreads = new long[9];
+
+ // error request per second
+ private long[] errorTPS = null;
+
+ // error response times per second
+ private long[] errorResponseTimes = null;
+
+ // tps per second
+ private long[] tps = null;
+
+ // response times per second
+ private long[] responseTimes = null;
+
+ // benchmark startTime
+ private long startTime;
+
+ // benchmark maxRange
+ private int maxRange;
+
+ public SimpleProcessorBenchmarkClientRunnable(String targetIP, int targetPort, int clientNums, int rpcTimeout,
+ CyclicBarrier barrier, CountDownLatch latch, long startTime,
+ long endTime){
+
+ this.targetIP = targetIP;
+ this.targetPort = targetPort;
+ this.clientNums = clientNums;
+ this.rpcTimeout = rpcTimeout;
+ this.barrier = barrier;
+ this.latch = latch;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ maxRange = (Integer.parseInt(String.valueOf((endTime - startTime))) / 1000) + 1;
+ errorTPS = new long[maxRange];
+ errorResponseTimes = new long[maxRange];
+ tps = new long[maxRange];
+ responseTimes = new long[maxRange];
+ // init
+ for (int i = 0; i < maxRange; i++) {
+ errorTPS[i] = 0;
+ errorResponseTimes[i] = 0;
+ tps[i] = 0;
+ responseTimes[i] = 0;
+ }
+ }
+
+ public void run() {
+ try {
+ barrier.await();
+ } catch (Exception e) {
+ // IGNORE
+ }
+ runJavaAndHessian();
+ latch.countDown();
+ }
+
+ private void runJavaAndHessian() {
+ while (running) {
+ Object requestObject = new RequestObject(requestSize);
+ long beginTime = System.nanoTime();
+ if (beginTime >= endTime) {
+ running = false;
+ break;
+ }
+ try {
+ Object response = null;
+ response = clientFactory.get(targetIP, targetPort, rpcTimeout, clientNums).request(requestObject).get();
+ long currentTime = System.nanoTime();
+ if (beginTime <= startTime) {
+ continue;
+ }
+ long consumeTime = currentTime - beginTime;
+ sumResponseTimeSpread(consumeTime);
+ int range = Integer.parseInt(String.valueOf(beginTime - startTime)) / 1000;
+ if (range >= maxRange) {
+ System.err.println("benchmark range exceeds maxRange,range is: " + range + ",maxRange is: "
+ + maxRange);
+ continue;
+ }
+ if (((ResponseObject) response).getBytes() != null) {
+ tps[range] = tps[range] + 1;
+ responseTimes[range] = responseTimes[range] + consumeTime;
+ } else {
+ LOGGER.error("server return response is null");
+ errorTPS[range] = errorTPS[range] + 1;
+ errorResponseTimes[range] = errorResponseTimes[range] + consumeTime;
+ }
+ } catch (Exception e) {
+ LOGGER.error("client.invokeSync error", e);
+ long currentTime = System.nanoTime();
+ if (beginTime <= startTime) {
+ continue;
+ }
+ long consumeTime = currentTime - beginTime;
+ sumResponseTimeSpread(consumeTime);
+ int range = Integer.parseInt(String.valueOf(beginTime - startTime)) / 1000;
+ if (range >= maxRange) {
+ System.err.println("benchmark range exceeds maxRange,range is: " + range + ",maxRange is: "
+ + maxRange);
+ continue;
+ }
+ errorTPS[range] = errorTPS[range] + 1;
+ errorResponseTimes[range] = errorResponseTimes[range] + consumeTime;
+ }
+ }
+ }
+
+ public List<long[]> getResults() {
+ List<long[]> results = new ArrayList<long[]>();
+ results.add(responseSpreads);
+ results.add(tps);
+ results.add(responseTimes);
+ results.add(errorTPS);
+ results.add(errorResponseTimes);
+ return results;
+ }
+
+ private void sumResponseTimeSpread(long responseTime) {
+ responseTime = responseTime / 1000;
+ if (responseTime <= 0) {
+ responseSpreads[0] = responseSpreads[0] + 1;
+ } else if (responseTime > 0 && responseTime <= 1) {
+ responseSpreads[1] = responseSpreads[1] + 1;
+ } else if (responseTime > 1 && responseTime <= 5) {
+ responseSpreads[2] = responseSpreads[2] + 1;
+ } else if (responseTime > 5 && responseTime <= 10) {
+ responseSpreads[3] = responseSpreads[3] + 1;
+ } else if (responseTime > 10 && responseTime <= 50) {
+ responseSpreads[4] = responseSpreads[4] + 1;
+ } else if (responseTime > 50 && responseTime <= 100) {
+ responseSpreads[5] = responseSpreads[5] + 1;
+ } else if (responseTime > 100 && responseTime <= 500) {
+ responseSpreads[6] = responseSpreads[6] + 1;
+ } else if (responseTime > 500 && responseTime <= 1000) {
+ responseSpreads[7] = responseSpreads[7] + 1;
+ } else if (responseTime > 1000) {
+ responseSpreads[8] = responseSpreads[8] + 1;
+ }
+ }
+
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/resources/ReadMe.txt b/dubbo-test/dubbo-test-benchmark/src/main/resources/ReadMe.txt
new file mode 100644
index 0000000..88cc032
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/resources/ReadMe.txt
@@ -0,0 +1,13 @@
+Ò»¡¢Ð½¨Ò»¸öbenchmark¹¤³Ì£¬Èçdemo.benchmark
+¶þ¡¢µ¼Èë×Ô¼º·þÎñµÄ½Ó¿Úapi°üºÍdubbo.benchmark.jar(½âѹdubbo.benchmark.tar.gz£¬ÔÚlibĿ¼ÏÂ)
+Èý¡¢Ð½¨Ò»¸öÀ࣬ʵÏÖAbstractClientRunnable
+ a¡¢ÊµÏÖ¸¸ÀàµÄ¹¹Ô캯Êý
+ b¡¢ÊµÏÖinvoke·½·¨£¬Í¨¹ýserviceFactory´´½¨±¾µØ½Ó¿Ú´úÀí£¬²¢ÊµÏÖ×Ô¼ºµÄÒµÎñÂß¼£¬ÈçÏÂ
+ public Object invoke(ServiceFactory serviceFactory) {
+ DemoService demoService = (DemoService) serviceFactory.get(DemoService.class);
+ return demoService.sendRequest("hello");
+ }
+ËÄ¡¢½«×Ô¼ºµÄbenchmark¹¤³Ì´ò³Éjar°ü,Èçdemo.benchmark.jar
+Îå¡¢½«demo.benchmark.jar·Åµ½dubbo.benchmark/libĿ¼ÏÂ
+Áù¡¢ÅäÖÃduubo.properties
+Æß¡¢ÔËÐÐrun.bat(windows)»òrun.sh(linux)
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/resources/dubbo.properties b/dubbo-test/dubbo-test-benchmark/src/main/resources/dubbo.properties
new file mode 100644
index 0000000..84a779f
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/resources/dubbo.properties
@@ -0,0 +1,14 @@
+#\u5b9e\u73b0\u7684benchmark runnable\u7c7b\u540d
+classname=
+#\u63d0\u4f9b\u8005ip
+serverip=
+#\u63d0\u4f9b\u8005\u7aef\u53e3
+serverport=
+#\u5ba2\u6237\u7aef\u5e76\u53d1\u6570
+concurrents=
+#\u5ba2\u6237\u7aef\u8d85\u65f6\u65f6\u95f4\uff0c\u5355\u4f4d\u6beb\u79d2
+timeout=
+#benchmark\u8fd0\u884c\u65f6\u95f4,\u5355\u4f4d\u79d2
+runtime=
+#\u8fde\u63a5\u6570
+connectionnums=
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/resources/run.bat b/dubbo-test/dubbo-test-benchmark/src/main/resources/run.bat
new file mode 100644
index 0000000..dad1879
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/resources/run.bat
@@ -0,0 +1 @@
+java -Xms512m -Xmx512m -Xmn128m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -Dwrite.statistics=true -Djava.ext.dirs="./lib" "com.alibaba.dubbo.rpc.benchmark.RpcBenchmarkClient" > "benchmark.log"
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/main/resources/run.sh b/dubbo-test/dubbo-test-benchmark/src/main/resources/run.sh
new file mode 100644
index 0000000..05f52bb
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/main/resources/run.sh
@@ -0,0 +1 @@
+java -Xms512m -Xmx512m -Xmn128m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -Dwrite.statistics=true -Djava.ext.dirs="./lib" "com.alibaba.dubbo.rpc.benchmark.RpcBenchmarkClient" > "benchmark.log" 2>&1 &
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/test/java/com/jingdong/client/DemoBenchmarkClientRunnable.java b/dubbo-test/dubbo-test-benchmark/src/test/java/com/jingdong/client/DemoBenchmarkClientRunnable.java
new file mode 100644
index 0000000..08e881a
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/test/java/com/jingdong/client/DemoBenchmarkClientRunnable.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2101 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jingdong.client;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import com.alibaba.dubbo.rpc.benchmark.AbstractClientRunnable;
+import com.alibaba.dubbo.rpc.benchmark.DemoService;
+import com.alibaba.dubbo.rpc.benchmark.ServiceFactory;
+
+/**
+ * DemoBenchmarkClient.java
+ * @author tony.chenl
+ */
+public class DemoBenchmarkClientRunnable extends AbstractClientRunnable{
+
+ public DemoBenchmarkClientRunnable(String targetIP, int targetPort, int clientNums, int rpcTimeout,
+ CyclicBarrier barrier, CountDownLatch latch, long startTime,
+ long endTime){
+ super(targetIP, targetPort, clientNums, rpcTimeout, barrier, latch, startTime, endTime);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public Object invoke(ServiceFactory serviceFactory) {
+ DemoService demoService = (DemoService) serviceFactory.get(DemoService.class);
+ return demoService.sendRequest("hello");
+ }
+
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/test/resources/BenchmarkRunner.java b/dubbo-test/dubbo-test-benchmark/src/test/resources/BenchmarkRunner.java
new file mode 100644
index 0000000..c28c5bd
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/test/resources/BenchmarkRunner.java
@@ -0,0 +1,1022 @@
+package com.dubbo.serialize.benchmark;
+
+import java.io.*;
+import java.net.URLEncoder;
+import java.util.*;
+import java.util.regex.Pattern;
+import java.util.zip.DeflaterOutputStream;
+
+import serializers.jackson.*;
+import serializers.json.JsonGsonDatabind;
+import serializers.json.JsonArgoTree;
+import serializers.json.FastJSONDatabind;
+import serializers.json.FlexjsonDatabind;
+import serializers.json.JsonGsonManual;
+import serializers.json.JsonGsonTree;
+import serializers.json.JsonDotOrgManualTree;
+import serializers.json.JsonLibJsonDatabind;
+import serializers.json.JsonPathDeserializerOnly;
+import serializers.json.JsonSimpleManualTree;
+import serializers.json.JsonSimpleWithContentHandler;
+import serializers.json.JsonSmartManualTree;
+import serializers.json.JsonTwoLattes;
+import serializers.json.JsonijJpath;
+import serializers.json.JsonijManualTree;
+import serializers.json.JsonSvensonDatabind;
+import serializers.protostuff.Protostuff;
+import serializers.protostuff.ProtostuffJson;
+import serializers.protostuff.ProtostuffSmile;
+import serializers.xml.XmlJavolution;
+import serializers.xml.XmlStax;
+import serializers.xml.XmlXStream;
+
+public class BenchmarkRunner
+{
+ public final static int DEFAULT_ITERATIONS = 2000;
+ public final static int DEFAULT_TRIALS = 20;
+
+ /**
+ * Number of milliseconds to warm up for each operation type for each serializer. Let's
+ * start with 3 seconds.
+ */
+ final static long DEFAULT_WARMUP_MSECS = 3000;
+
+ // These tests aren't included by default. Use the "-hidden" flag to enable them.
+ private static final HashSet<String> HIDDEN = new HashSet<String>();
+ static {
+ // CKS is not included because it's not really publicly released.
+ HIDDEN.add("cks");
+ HIDDEN.add("cks-text");
+ }
+
+ private static final String ERROR_DIVIDER = "-------------------------------------------------------------------";
+
+ public static void main(String[] args)
+ {
+ // --------------------------------------------------
+ // Parse command-line options.
+
+ Boolean filterIsInclude = null;
+ Set<String> filterStrings = null;
+ Integer iterations = null;
+ Integer trials = null;
+ Long warmupTime = null;
+ boolean printChart = false;
+ boolean prewarm = false;
+ String dataFileName = null;
+ boolean enableHidden = false;
+
+ Set<String> optionsSeen = new HashSet<String>();
+
+ for (String arg : args) {
+ String remainder;
+ if (arg.startsWith("--")) {
+ remainder = arg.substring(2);
+ }
+ else if (arg.startsWith("-")) {
+ remainder = arg.substring(1);
+ }
+ else if (dataFileName == null) {
+ dataFileName = arg;
+ continue;
+ }
+ else {
+ System.err.println("Expecting only one non-option argument (<data-file> = \"" + dataFileName + "\").");
+ System.err.println("Found a second one: \"" + arg + "\"");
+ System.err.println("Use \"-help\" for usage information.");
+ System.exit(1); return;
+ }
+
+ String option, value;
+ int eqPos = remainder.indexOf('=');
+ if (eqPos >= 0) {
+ option = remainder.substring(0, eqPos);
+ value = remainder.substring(eqPos+1);
+ } else {
+ option = remainder;
+ value = null;
+ }
+
+ if (!optionsSeen.add(option)) {
+ System.err.println("Repeated option: \"" + arg + "\"");
+ System.exit(1); return;
+ }
+
+ if (option.equals("include")) {
+ if (value == null) {
+ System.err.println("The \"include\" option requires a value.");
+ System.exit(1); return;
+ }
+ if (filterIsInclude == null) {
+ filterIsInclude = true;
+ filterStrings = new HashSet<String>(Arrays.asList(value.split(",")));
+ } else {
+ System.err.println("Can't use 'include' and 'exclude' options at the same time.");
+ System.exit(1); return;
+ }
+ }
+ else if (option.equals("exclude")) {
+ if (value == null) {
+ System.err.println("The \"exclude\" option requires a value.");
+ System.exit(1); return;
+ }
+ if (filterIsInclude == null) {
+ filterIsInclude = false;
+ filterStrings = new HashSet<String>(Arrays.asList(value.split(",")));
+ } else {
+ System.err.println("Can't use 'include' and 'exclude' options at the same time.");
+ System.exit(1); return;
+ }
+ }
+ else if (option.equals("iterations")) {
+ if (value == null) {
+ System.err.println("The \"iterations\" option requires a value.");
+ System.exit(1); return;
+ }
+ assert iterations == null;
+ try {
+ iterations = Integer.parseInt(value);
+ } catch (NumberFormatException ex) {
+ System.err.println("Invalid value for \"iterations\" option: \"" + value + "\"");
+ System.exit(1); return;
+ }
+ if (iterations < 1) {
+ System.err.println("Invalid value for \"iterations\" option: \"" + value + "\"");
+ System.exit(1); return;
+ }
+ }
+ else if (option.equals("trials")) {
+ if (value == null) {
+ System.err.println("The \"trials\" option requires a value.");
+ System.exit(1); return;
+ }
+ assert trials == null;
+ try {
+ trials = Integer.parseInt(value);
+ } catch (NumberFormatException ex) {
+ System.err.println("Invalid value for \"trials\" option: \"" + value + "\"");
+ System.exit(1); return;
+ }
+ if (trials < 1) {
+ System.err.println("Invalid value for \"trials\" option: \"" + value + "\"");
+ System.exit(1); return;
+ }
+ }
+ else if (option.equals("warmup-time")) {
+ if (value == null) {
+ System.err.println("The \"warmup-time\" option requires a value.");
+ System.exit(1); return;
+ }
+ assert warmupTime == null;
+ try {
+ warmupTime = Long.parseLong(value);
+ } catch (NumberFormatException ex) {
+ System.err.println("Invalid value for \"warmup-time\" option: \"" + value + "\"");
+ System.exit(1); return;
+ }
+ if (warmupTime < 0) {
+ System.err.println("Invalid value for \"warmup-time\" option: \"" + value + "\"");
+ System.exit(1); return;
+ }
+ }
+ else if (option.equals("pre-warmup")) {
+ if (value != null) {
+ System.err.println("The \"pre-warmup\" option does not take a value: \"" + arg + "\"");
+ System.exit(1); return;
+ }
+ assert !prewarm;
+ prewarm = true;
+ }
+ else if (option.equals("chart")) {
+ if (value != null) {
+ System.err.println("The \"chart\" option does not take a value: \"" + arg + "\"");
+ System.exit(1); return;
+ }
+ assert !printChart;
+ printChart = true;
+ }
+ else if (option.equals("hidden")) {
+ if (value != null) {
+ System.err.println("The \"hidden\" option does not take a value: \"" + arg + "\"");
+ System.exit(1); return;
+ }
+ assert !enableHidden;
+ enableHidden = true;
+ }
+ else if (option.equals("help")) {
+ if (value != null) {
+ System.err.println("The \"help\" option does not take a value: \"" + arg + "\"");
+ System.exit(1); return;
+ }
+ if (args.length != 1) {
+ System.err.println("The \"help\" option cannot be combined with any other option.");
+ System.exit(1); return;
+ }
+
+ System.out.println();
+ System.out.println("Usage: run [options] <data-file>");
+ System.out.println();
+ System.out.println("Options:");
+ System.out.println(" -iterations=n [default=" + DEFAULT_ITERATIONS + "]");
+ System.out.println(" -trials=n [default=" + DEFAULT_TRIALS + "]");
+ System.out.println(" -warmup-time=millis [default=" + DEFAULT_WARMUP_MSECS + "]");
+ System.out.println(" -pre-warmup (warm all serializers before the first measurement)");
+ System.out.println(" -chart (generate a Google Chart URL for the results)");
+ System.out.println(" -include=impl1,impl2,impl3,...");
+ System.out.println(" -exclude=impl1,impl2,impl3,...");
+ System.out.println(" -hidden (enable \"hidden\" serializers)");
+ System.out.println(" -help");
+ System.out.println();
+ System.out.println("Example: run -chart -include=protobuf,thrift data/media.1.cks");
+ System.out.println();
+ System.exit(0); return;
+ }
+ else {
+ System.err.println("Unknown option: \"" + arg + "\"");
+ System.err.println("Use \"-help\" for usage information.");
+ System.exit(1); return;
+ }
+ }
+
+ if (iterations == null) iterations = DEFAULT_ITERATIONS;
+ if (trials == null) trials = DEFAULT_TRIALS;
+ if (warmupTime == null) warmupTime = DEFAULT_WARMUP_MSECS;
+
+ if (dataFileName == null) {
+ System.err.println("Missing <data-file> argument.");
+ System.err.println("Use \"-help\" for usage information.");
+ System.exit(1); return;
+ }
+
+ // --------------------------------------------------
+ // Load serializers.
+
+ TestGroups groups = new TestGroups();
+
+ // Binary Formats; language-specific ones
+ JavaBuiltIn.register(groups);
+ JavaManual.register(groups);
+ Scala.register(groups);
+ // hessian and kryo are Java object serializations
+ Hessian.register(groups);
+ Dubbo.register(groups);
+ Kryo.register(groups);
+ Wobly.register(groups);
+
+ // Binary formats, generic: protobuf, thrift, avro, kryo, CKS, msgpack
+ Protobuf.register(groups);
+ ActiveMQProtobuf.register(groups);
+ Protostuff.register(groups);
+ Thrift.register(groups);
+ AvroSpecific.register(groups);
+ AvroGeneric.register(groups);
+ CksBinary.register(groups);
+ MsgPack.register(groups);
+
+ // JSON
+ JacksonJsonManual.register(groups);
+ JacksonJsonTree.register(groups);
+ JacksonJsonTreeWithStrings.register(groups);
+ JacksonJsonDatabind.register(groups);
+ JacksonJsonDatabindWithStrings.register(groups);
+ JsonTwoLattes.register(groups);
+ ProtostuffJson.register(groups);
+ ProtobufJson.register(groups);
+ JsonGsonManual.register(groups);
+ JsonGsonTree.register(groups);
+ JsonGsonDatabind.register(groups);
+ JsonSvensonDatabind.register(groups);
+ FlexjsonDatabind.register(groups);
+ JsonLibJsonDatabind.register(groups);
+ FastJSONDatabind.register(groups);
+ JsonSimpleWithContentHandler.register(groups);
+ JsonSimpleManualTree.register(groups);
+ JsonSmartManualTree.register(groups);
+ JsonDotOrgManualTree.register(groups);
+ JsonijJpath.register(groups);
+ JsonijManualTree.register(groups);
+ JsonArgoTree.register(groups);
+ JsonPathDeserializerOnly.register(groups);
+ // Then JSON-like
+ // CKS text is textual JSON-like format
+ CksText.register(groups);
+ // then binary variants
+ // BSON is binary JSON-like format
+ JacksonBsonManual.register(groups);
+ JacksonBsonDatabind.register(groups);
+ MongoDB.register(groups);
+ // Smile is 1-to-1 binary representation of JSON
+ JacksonSmileManual.register(groups);
+ JacksonSmileDatabind.register(groups);
+ ProtostuffSmile.register(groups);
+
+ // XML-based formats.
+ XmlStax.register(groups);
+ XmlXStream.register(groups);
+ JacksonXmlDatabind.register(groups);
+ XmlJavolution.register(groups);
+
+ // --------------------------------------------------
+ // Load data value.
+
+ Object dataValue;
+ TestGroup<?> group;
+ {
+ File dataFile = new File(dataFileName);
+ if (!dataFile.exists()) {
+ System.out.println("Couldn't find data file \"" + dataFile.getPath() + "\"");
+ System.exit(1); return;
+ }
+
+ String[] parts = dataFile.getName().split("\\.");
+ if (parts.length < 3) {
+ System.out.println("Data file \"" + dataFile.getName() + "\" should be of the form \"<type>.<name>.<extension>\"");
+ System.exit(1); return;
+ }
+
+ String dataType = parts[0];
+ String extension = parts[parts.length-1];
+
+ group = groups.groupMap.get(dataType);
+ if (group == null) {
+ System.out.println("Data file \"" + dataFileName + "\" can't be loaded.");
+ System.out.println("Don't know about data type \"" + dataType + "\"");
+ System.exit(1); return;
+ }
+
+ TestGroup.Entry<?,Object> loader = group.extensionMap.get(parts[parts.length-1]);
+ if (loader == null) {
+ System.out.println("Data file \"" + dataFileName + "\" can't be loaded.");
+ System.out.println("No deserializer registered for data type \"" + dataType + "\" and file extension \"." + extension + "\"");
+ System.exit(1); return;
+ }
+
+
+ Object deserialized;
+ try {
+ byte[] fileBytes = readFile(new File(dataFileName)); // Load entire file into a byte array.
+ deserialized = loader.serializer.deserialize(fileBytes);
+ }
+ catch (Exception ex) {
+ System.err.println("Error loading data from file \"" + dataFileName + "\".");
+ System.err.println(ex.getMessage());
+ System.exit(1); return;
+ }
+
+ dataValue = loader.transformer.reverse(deserialized);
+ }
+
+ @SuppressWarnings("unchecked")
+ TestGroup<Object> group_ = (TestGroup<Object>) group;
+
+ // --------------------------------------------------
+
+ Set<String> matched = new HashSet<String>();
+
+ Iterable<TestGroup.Entry<Object,Object>> available;
+
+ if (enableHidden) {
+ // Use all of them.
+ available = group_.entries;
+ } else {
+ // Remove the hidden ones.
+ ArrayList<TestGroup.Entry<Object,Object>> unhidden = new ArrayList<TestGroup.Entry<Object,Object>>();
+ for (TestGroup.Entry<?,Object> entry_ : group.entries) {
+ @SuppressWarnings("unchecked")
+ TestGroup.Entry<Object,Object> entry = (TestGroup.Entry<Object,Object>) entry_;
+ String name = entry.serializer.getName();
+ if (!HIDDEN.contains(name)) unhidden.add(entry);
+ }
+ available = unhidden;
+ }
+
+ Iterable<TestGroup.Entry<Object,Object>> matchingEntries;
+ if (filterStrings == null) {
+ matchingEntries = available;
+ }
+ else {
+ ArrayList<TestGroup.Entry<Object,Object>> al = new ArrayList<TestGroup.Entry<Object,Object>>();
+ matchingEntries = al;
+
+ for (TestGroup.Entry<?,Object> entry_ : available) {
+ @SuppressWarnings("unchecked")
+ TestGroup.Entry<Object,Object> entry = (TestGroup.Entry<Object,Object>) entry_;
+
+ String name = entry.serializer.getName();
+
+ // See if any of the filters match.
+ boolean found = false;
+ for (String s : filterStrings) {
+ boolean thisOneMatches = match(s, name);
+ if (thisOneMatches) {
+ matched.add(s);
+ found = true;
+ }
+ }
+
+ if (found == filterIsInclude) {
+ al.add(entry);
+ }
+ }
+
+ Set<String> unmatched = new HashSet<String>(filterStrings);
+ unmatched.removeAll(matched);
+ for (String s : unmatched) {
+ System.err.println("Warning: there is no implementation name matching the pattern \"" + s + "\"");
+
+ if (!enableHidden) {
+ for (String hiddenName : HIDDEN) {
+ if (match(s, hiddenName)) {
+ System.err.println("(The \"" + hiddenName + "\", serializer is hidden by default.");
+ System.err.println(" Use the \"-hidden\" option to enable hidden serializers)");
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ EnumMap<measurements, Map<String, Double>> values;
+ StringWriter errors = new StringWriter();
+ PrintWriter errorsPW = new PrintWriter(errors);
+ try {
+ values = start(errorsPW, iterations, trials, warmupTime, prewarm, matchingEntries, dataValue);
+ }
+ catch (Exception ex) {
+ ex.printStackTrace(System.err);
+ System.exit(1); return;
+ }
+
+ if (printChart) {
+ printImages(values);
+ }
+
+ // Print errors after chart. That way you can't miss it.
+ String errorsString = errors.toString();
+ if (errorsString.length() > 0) {
+ System.out.println(ERROR_DIVIDER);
+ System.out.println("Errors occurred during benchmarking:");
+ System.out.print(errorsString);
+ System.exit(1); return;
+ }
+ }
+
+ private static boolean match(String pattern, String name)
+ {
+ StringBuilder regex = new StringBuilder();
+
+ while (pattern.length() > 0) {
+ int starPos = pattern.indexOf('*');
+ if (starPos < 0) {
+ regex.append(Pattern.quote(pattern));
+ break;
+ }
+ else {
+ String beforeStar = pattern.substring(0, starPos);
+ String afterStar = pattern.substring(starPos + 1);
+
+ regex.append(Pattern.quote(beforeStar));
+ regex.append(".*");
+ pattern = afterStar;
+ }
+ }
+
+ return Pattern.matches(regex.toString(), name);
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ private static byte[] readFile(File file)
+ throws IOException
+ {
+ FileInputStream fin = new FileInputStream(file);
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+ byte[] data = new byte[1024];
+ while (true) {
+ int numBytes = fin.read(data);
+ if (numBytes < 0) break;
+ baos.write(data, 0, numBytes);
+ }
+ return baos.toByteArray();
+ }
+ finally {
+ fin.close();
+ }
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ private static double iterationTime(long delta, int iterations)
+ {
+ return (double) delta / (double) (iterations);
+ }
+
+ private static final TestCase Create = new TestCase()
+ {
+ public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
+ {
+ long start = System.nanoTime();
+ for (int i = 0; i < iterations; i++)
+ {
+ transformer.forward(value);
+ }
+ return iterationTime(System.nanoTime() - start, iterations);
+ }
+ };
+
+ private static final TestCase Serialize = new TestCase()
+ {
+ public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
+ {
+ long start = System.nanoTime();
+ for (int i = 0; i < iterations; i++)
+ {
+ Object obj = transformer.forward(value);
+ serializer.serialize(obj);
+ }
+ return iterationTime(System.nanoTime() - start, iterations);
+ }
+ };
+
+ private static final TestCase SerializeSameObject = new TestCase()
+ {
+ public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
+ {
+ // let's reuse same instance to reduce overhead
+ Object obj = transformer.forward(value);
+ long start = System.nanoTime();
+ for (int i = 0; i < iterations; i++)
+ {
+ serializer.serialize(obj);
+ //if (i % 1000 == 0)
+ // doGc();
+ }
+ return iterationTime(System.nanoTime() - start, iterations);
+ }
+ };
+
+ private static final TestCase Deserialize = new TestCase()
+ {
+ public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
+ {
+ byte[] array = serializer.serialize(transformer.forward(value));
+ long start = System.nanoTime();
+ for (int i = 0; i < iterations; i++)
+ {
+ serializer.deserialize(array);
+ }
+ return iterationTime(System.nanoTime() - start, iterations);
+ }
+ };
+
+ private static final TestCase DeserializeAndCheck = new TestCase()
+ {
+ public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
+ {
+ byte[] array = serializer.serialize(transformer.forward(value));
+ long start = System.nanoTime();
+ for (int i = 0; i < iterations; i++)
+ {
+ Object obj = serializer.deserialize(array);
+ transformer.reverse(obj);
+ }
+ return iterationTime(System.nanoTime() - start, iterations);
+ }
+ };
+
+ private static final TestCase DeserializeAndCheckShallow = new TestCase()
+ {
+ public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
+ {
+ byte[] array = serializer.serialize(transformer.forward(value));
+ long start = System.nanoTime();
+ for (int i = 0; i < iterations; i++)
+ {
+ Object obj = serializer.deserialize(array);
+ transformer.shallowReverse(obj);
+ }
+ return iterationTime(System.nanoTime() - start, iterations);
+ }
+ };
+
+ /**
+ * JVM is not required to honor GC requests, but adding bit of sleep around request is
+ * most likely to give it a chance to do it.
+ */
+ private static void doGc()
+ {
+ try {
+ Thread.sleep(50L);
+ } catch (InterruptedException ie) {
+ System.err.println("Interrupted while sleeping in serializers.BenchmarkRunner.doGc()");
+ }
+ System.gc();
+ try { // longer sleep afterwards (not needed by GC, but may help with scheduling)
+ Thread.sleep(200L);
+ } catch (InterruptedException ie) {
+ System.err.println("Interrupted while sleeping in serializers.BenchmarkRunner.doGc()");
+ }
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ private static abstract class TestCase
+ {
+ public abstract <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception;
+ }
+
+ private static final class TestCaseRunner<J>
+ {
+ private final Transformer<J,Object> transformer;
+ private final Serializer<Object> serializer;
+ private final J value;
+
+ public TestCaseRunner(Transformer<J,Object> transformer, Serializer<Object> serializer, J value)
+ {
+ this.transformer = transformer;
+ this.serializer = serializer;
+ this.value = value;
+ }
+
+ public double run(TestCase tc, int iterations) throws Exception
+ {
+ return tc.run(transformer, serializer, value, iterations);
+ }
+
+ public double runTakeMin(int trials, TestCase tc, int iterations) throws Exception
+ {
+ double minTime = Double.MAX_VALUE;
+ for (int i = 0; i < trials; i++) {
+ double time = tc.run(transformer, serializer, value, iterations);
+ minTime = Math.min(minTime, time);
+ }
+ return minTime;
+ }
+ }
+
+ enum measurements
+ {
+ timeCreate("create (nanos)"), timeSerializeDifferentObjects("ser (nanos)"), timeSerializeSameObject("ser+same (nanos)"),
+ timeDeserializeNoFieldAccess("deser (nanos)"), timeDeserializeAndCheck("deser+deep (nanos)"), timeDeserializeAndCheckShallow("deser+shal (nanos)"),
+ totalTime("total (nanos)"), length("size (bytes)"), lengthDeflate("size+dfl (bytes)"),
+ ;
+
+ public final String displayName;
+
+ measurements(String displayName)
+ {
+ this.displayName = displayName;
+ }
+ }
+
+ private static <J> EnumMap<measurements, Map<String, Double>>
+ start(PrintWriter errors, int iterations, int trials, long warmupTime, boolean prewarm, Iterable<TestGroup.Entry<J,Object>> groups, J value) throws Exception
+ {
+ // Check correctness first.
+ System.out.println("Checking correctness...");
+ for (TestGroup.Entry<J,Object> entry : groups)
+ {
+ checkCorrectness(errors, entry.transformer, entry.serializer, value);
+ }
+ System.out.println("[done]");
+
+ // Pre-warm.
+ if (prewarm) {
+ System.out.print("Pre-warmup...");
+ for (TestGroup.Entry<J,Object> entry : groups)
+ {
+ TestCaseRunner<J> runner = new TestCaseRunner<J>(entry.transformer, entry.serializer, value);
+ String name = entry.serializer.getName();
+ System.out.print(" " + name);
+
+ warmCreation(runner, warmupTime);
+ warmSerialization(runner, warmupTime);
+ warmDeserialization(runner, warmupTime);
+ }
+ System.out.println();
+ System.out.println("[done]");
+ }
+
+ System.out.printf("%-32s %6s %7s %7s %7s %7s %7s %7s %6s %5s\n",
+ "",
+ "create",
+ "ser",
+ "+same",
+ "deser",
+ "+shal",
+ "+deep",
+ "total",
+ "size",
+ "+dfl");
+ EnumMap<measurements, Map<String, Double>> values = new EnumMap<measurements, Map<String, Double>>(measurements.class);
+ for (measurements m : measurements.values())
+ values.put(m, new HashMap<String, Double>());
+
+ // Actual tests.
+ for (TestGroup.Entry<J,Object> entry : groups)
+ {
+ TestCaseRunner<J> runner = new TestCaseRunner<J>(entry.transformer, entry.serializer, value);
+ String name = entry.serializer.getName();
+ try {
+
+ /*
+ * Should only warm things for the serializer that we test next: HotSpot JIT will
+ * otherwise spent most of its time optimizing slower ones... Use
+ * -XX:CompileThreshold=1 to hint the JIT to start immediately
+ *
+ * Actually: 1 is often not a good value -- threshold is the number
+ * of samples needed to trigger inlining, and there's no point in
+ * inlining everything. Default value is in thousands, so lowering
+ * it to, say, 1000 is usually better.
+ */
+ warmCreation(runner, warmupTime);
+
+ doGc();
+ double timeCreate = runner.runTakeMin(trials, Create, iterations * 100); // do more iteration for object creation because of its short time
+
+ warmSerialization(runner, warmupTime);
+
+ doGc();
+ double timeSerializeDifferentObjects = runner.runTakeMin(trials, Serialize, iterations);
+
+ doGc();
+ double timeSerializeSameObject = runner.runTakeMin(trials, SerializeSameObject, iterations);
+
+ warmDeserialization(runner, warmupTime);
+
+ doGc();
+ double timeDeserializeNoFieldAccess = runner.runTakeMin(trials, Deserialize, iterations);
+
+ doGc();
+ double timeDeserializeAndCheckShallow = runner.runTakeMin(trials, DeserializeAndCheckShallow, iterations);
+
+ doGc();
+ double timeDeserializeAndCheck = runner.runTakeMin(trials, DeserializeAndCheck, iterations);
+
+ double totalTime = timeSerializeDifferentObjects + timeDeserializeAndCheck;
+
+ byte[] array = entry.serializer.serialize(entry.transformer.forward(value));
+
+ byte[] compressDeflate = compressDeflate(array);
+
+ System.out.printf("%-32s %6.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %6d %5d\n",
+ name,
+ timeCreate,
+ timeSerializeDifferentObjects,
+ timeSerializeSameObject,
+ timeDeserializeNoFieldAccess,
+ timeDeserializeAndCheckShallow,
+ timeDeserializeAndCheck,
+ totalTime,
+ array.length,
+ compressDeflate.length);
+
+ addValue(values, name, timeCreate, timeSerializeDifferentObjects, timeSerializeSameObject,
+ timeDeserializeNoFieldAccess, timeDeserializeAndCheckShallow, timeDeserializeAndCheck, totalTime,
+ array.length, compressDeflate.length);
+ }
+ catch (Exception ex) {
+ System.out.println("ERROR: \"" + name + "\" crashed during benchmarking.");
+ errors.println(ERROR_DIVIDER);
+ errors.println("\"" + name + "\" crashed during benchmarking.");
+ ex.printStackTrace(errors);
+ }
+ }
+
+ return values;
+ }
+
+ private static byte[] compressDeflate(byte[] data)
+ {
+ try {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream(500);
+ DeflaterOutputStream compresser = new DeflaterOutputStream(bout);
+ compresser.write(data, 0, data.length);
+ compresser.finish();
+ compresser.flush();
+ return bout.toByteArray();
+ }
+ catch (IOException ex) {
+ AssertionError ae = new AssertionError("IOException while writing to ByteArrayOutputStream!");
+ ae.initCause(ex);
+ throw ae;
+ }
+ }
+
+ /**
+ * Method that tries to validate correctness of serializer, using
+ * round-trip (construct, serializer, deserialize; compare objects
+ * after steps 1 and 3).
+ */
+ private static <J> void checkCorrectness(PrintWriter errors, Transformer<J,Object> transformer, Serializer<Object> serializer, J value)
+ throws Exception
+ {
+ Object specialInput;
+ String name = serializer.getName();
+
+ try {
+ specialInput = transformer.forward(value);
+ }
+ catch (Exception ex) {
+ System.out.println("ERROR: \"" + name + "\" crashed during forward transformation.");
+ errors.println(ERROR_DIVIDER);
+ errors.println("\"" + name + "\" crashed during forward transformation.");
+ ex.printStackTrace(errors);
+ return;
+ }
+
+ byte[] array;
+
+ try {
+ array = serializer.serialize(specialInput);
+ }
+ catch (Exception ex) {
+ System.out.println("ERROR: \"" + name + "\" crashed during serialization.");
+ errors.println(ERROR_DIVIDER);
+ errors.println("\"" + name + "\" crashed during serialization.");
+ ex.printStackTrace(errors);
+ return;
+ }
+
+ Object specialOutput;
+
+ try {
+ specialOutput = serializer.deserialize(array);
+ }
+ catch (Exception ex) {
+ System.out.println("ERROR: \"" + name + "\" crashed during deserialization.");
+ errors.println(ERROR_DIVIDER);
+ errors.println("\"" + name + "\" crashed during deserialization.");
+ ex.printStackTrace(errors);
+ return;
+ }
+
+ J output;
+ try {
+ output = transformer.reverse(specialOutput);
+ }
+ catch (Exception ex) {
+ System.out.println("ERROR: \"" + name + "\" crashed during reverse transformation.");
+ errors.println(ERROR_DIVIDER);
+ errors.println("\"" + name + "\" crashed during reverse transformation.");
+ ex.printStackTrace(errors);
+ return;
+ }
+
+
+ if (!value.equals(output)) {
+ System.out.println("ERROR: \"" + name + "\" failed round-trip check.");
+ errors.println(ERROR_DIVIDER);
+ errors.println("\"" + name + "\" failed round-trip check.");
+ errors.println("ORIGINAL: " + value);
+ errors.println("ROUNDTRIP: " + output);
+ }
+ }
+
+ private static void printImages(EnumMap<measurements, Map<String, Double>> values)
+ {
+ for (measurements m : values.keySet()) {
+ Map<String, Double> map = values.get(m);
+ ArrayList<Map.Entry<String,Double>> list = new ArrayList<Map.Entry<String,Double>>(map.entrySet());
+ Collections.sort(list, new Comparator<Map.Entry<String,Double>>() {
+ public int compare (Map.Entry<String,Double> o1, Map.Entry<String,Double> o2) {
+ double diff = o1.getValue() - o2.getValue();
+ return diff > 0 ? 1 : (diff < 0 ? -1 : 0);
+ }
+ });
+ LinkedHashMap<String, Double> sortedMap = new LinkedHashMap<String, Double>();
+ for (Map.Entry<String, Double> entry : list) {
+ if( !entry.getValue().isNaN() ) {
+ sortedMap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ if (!sortedMap.isEmpty()) printImage(sortedMap, m);
+ }
+ }
+
+ private static String urlEncode(String s)
+ {
+ try {
+ return URLEncoder.encode(s, "UTF-8");
+ }
+ catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void printImage(Map<String, Double> map, measurements m)
+ {
+ StringBuilder valSb = new StringBuilder();
+ String names = "";
+ double max = Double.MIN_NORMAL;
+ for (Map.Entry<String, Double> entry : map.entrySet())
+ {
+ double value = entry.getValue();
+ valSb.append((int) value).append(',');
+ max = Math.max(max, entry.getValue());
+ names = urlEncode(entry.getKey()) + '|' + names;
+ }
+
+ int headerSize = 30;
+
+ int maxPixels = 300 * 1000; // Limit set by Google's Chart API.
+
+ int maxHeight = 600;
+ int width = maxPixels / maxHeight;
+
+ int barThickness = 10;
+ int barSpacing = 10;
+
+ int height;
+
+ // Reduce bar thickness and spacing until we can fit in the maximum height.
+ while (true) {
+ height = headerSize + map.size()*(barThickness + barSpacing);
+ if (height <= maxHeight) break;
+ barSpacing--;
+ if (barSpacing == 1) break;
+
+ height = headerSize + map.size()*(barThickness + barSpacing);
+ if (height <= maxHeight) break;
+ barThickness--;
+ if (barThickness == 1) break;
+ }
+
+ boolean truncated = false;
+ if (height > maxHeight) {
+ truncated = true;
+ height = maxHeight;
+ }
+
+ double scale = max * 1.1;
+ System.out.println("<img src='https://chart.googleapis.com/chart?chtt="
+ + urlEncode(m.displayName)
+ + "&chf=c||lg||0||FFFFFF||1||76A4FB||0|bg||s||EFEFEF&chs="+width+"x"+height+"&chd=t:"
+ + valSb.toString().substring(0, valSb.length() - 1)
+ + "&chds=0,"+ scale
+ + "&chxt=y"
+ + "&chxl=0:|" + names.substring(0, names.length() - 1)
+ + "&chm=N *f*,000000,0,-1,10&lklk&chdlp=t&chco=660000|660033|660066|660099|6600CC|6600FF|663300|663333|663366|663399|6633CC|6633FF|666600|666633|666666&cht=bhg&chbh=" + barThickness + ",0," + barSpacing + "&nonsense=aaa.png'/>");
+
+ if (truncated) {
+ System.err.println("WARNING: Not enough room to fit all bars in chart.");
+ }
+ }
+
+ private static void addValue(
+ EnumMap<measurements, Map<String, Double>> values,
+ String name,
+ double timeCreate,
+ double timeSerializeDifferentObjects,
+ double timeSerializeSameObject,
+ double timeDeserializeNoFieldAccess,
+ double timeDeserializeAndCheckShallow,
+ double timeDeserializeAndCheck,
+ double totalTime,
+ double length, double lengthDeflate)
+ {
+ values.get(measurements.timeSerializeDifferentObjects).put(name, timeSerializeDifferentObjects);
+ values.get(measurements.timeSerializeSameObject).put(name, timeSerializeSameObject);
+ values.get(measurements.timeDeserializeNoFieldAccess).put(name, timeDeserializeNoFieldAccess);
+ values.get(measurements.timeDeserializeAndCheckShallow).put(name, timeDeserializeAndCheckShallow);
+ values.get(measurements.timeDeserializeAndCheck).put(name, timeDeserializeAndCheck);
+ values.get(measurements.totalTime).put(name, totalTime);
+ values.get(measurements.length).put(name, length);
+ values.get(measurements.lengthDeflate).put(name, lengthDeflate);
+ values.get(measurements.timeCreate).put(name, timeCreate);
+ }
+
+ private static <J> void warmCreation(TestCaseRunner<J> runner, long warmupTime) throws Exception
+ {
+ // Instead of fixed counts, let's try to prime by running for N seconds
+ long endTime = System.currentTimeMillis() + warmupTime;
+ do
+ {
+ runner.run(Create, 10);
+ }
+ while (System.currentTimeMillis() < endTime);
+ }
+
+ private static <J> void warmSerialization(TestCaseRunner<J> runner, long warmupTime) throws Exception
+ {
+ // Instead of fixed counts, let's try to prime by running for N seconds
+ long endTime = System.currentTimeMillis() + warmupTime;
+ do
+ {
+ runner.run(Serialize, 10);
+ }
+ while (System.currentTimeMillis() < endTime);
+ }
+
+ private static <J> void warmDeserialization(TestCaseRunner<J> runner, long warmupTime) throws Exception
+ {
+ // Instead of fixed counts, let's try to prime by running for N seconds
+ long endTime = System.currentTimeMillis() + warmupTime;
+ do
+ {
+ runner.run(DeserializeAndCheck, 10);
+ }
+ while (System.currentTimeMillis() < endTime);
+ }
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/test/resources/Dubbo.java b/dubbo-test/dubbo-test-benchmark/src/test/resources/Dubbo.java
new file mode 100644
index 0000000..4be9444
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/test/resources/Dubbo.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2011 Alibaba.com All right reserved. This software is the
+ * confidential and proprietary information of Alibaba.com ("Confidential
+ * Information"). You shall not disclose such Confidential Information and shall
+ * use it only in accordance with the terms of the license agreement you entered
+ * into with Alibaba.com.
+ */
+package com.dubbo.serialize.benchmark;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.serialize.support.dubbo.GenericObjectInput;
+import com.alibaba.dubbo.common.serialize.support.dubbo.GenericObjectOutput;
+import com.caucho.hessian.io.Hessian2StreamingInput;
+import com.caucho.hessian.io.Hessian2StreamingOutput;
+
+import data.media.MediaContent;
+
+/**
+ * 类Dubbo.java的实现描述:Dubbo Seriazition Benchmark
+ *
+ * @author tony.chenl 2011-9-30 上午10:17:21
+ */
+public class Dubbo {
+
+ public static void register(TestGroups groups) {
+ groups.media.add(JavaBuiltIn.MediaTransformer, Dubbo.<MediaContent>GenericSerializer());
+ }
+
+ public static <T> Serializer<T> GenericSerializer()
+ {
+ @SuppressWarnings("unchecked")
+ Serializer<T> s = (Serializer<T>) GenericSerializer;
+ return s;
+ }
+
+ // ------------------------------------------------------------
+ // Serializer (just one)
+
+ public static Serializer<Object> GenericSerializer = new Serializer<Object>()
+ {
+ public Object deserialize(byte[] array) throws Exception
+ {
+ GenericObjectInput objectInput = new GenericObjectInput(new ByteArrayInputStream(array));
+ return objectInput.readObject();
+ }
+
+ public byte[] serialize(Object data) throws java.io.IOException
+ {
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(10240);
+ GenericObjectOutput objectOutput = new GenericObjectOutput(os);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+ return os.toByteArray();
+ }
+
+ public String getName()
+ {
+ return "dubbo";
+ }
+ };
+}
diff --git a/dubbo-test/dubbo-test-benchmark/src/test/resources/ProviderSample.xml b/dubbo-test/dubbo-test-benchmark/src/test/resources/ProviderSample.xml
new file mode 100644
index 0000000..8b65d30
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/test/resources/ProviderSample.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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.xsd
+ http://code.alibabatech.com/schema/dubbo
+ http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+ <bean id="demo.local" class="com.alibaba.dubbo.rpc.benchmark.DemoServiceImpl" />
+
+ <bean id="persistPropertyPlaceholderConfigurer"
+ class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+ <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+ <property name="ignoreResourceNotFound" value="true" />
+ <property name="ignoreUnresolvablePlaceholders" value="true" />
+ <property name="locations">
+ <list>
+ <value>classpath:dubbo-default.properties</value>
+ <value>classpath:dubbo.properties</value>
+ </list>
+ </property>
+ </bean>
+
+ <dubbo:registry address="N/A"/>
+
+ <!-- 服务应用配置 -->
+ <dubbo:application name="dubbo_provider" />
+
+ <!-- 服务提供者全局配置 -->
+ <dubbo:protocol name="dubbo" port="20885"/>
+
+ <!-- 服务提供者暴露服务配置 -->
+ <dubbo:service id="helloService" interface="com.alibaba.dubbo.rpc.benchmark.DemoService"
+ ref="demo.local"/>
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/test/resources/dubbo-default.properties b/dubbo-test/dubbo-test-benchmark/src/test/resources/dubbo-default.properties
new file mode 100644
index 0000000..e185bed
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/test/resources/dubbo-default.properties
@@ -0,0 +1,7 @@
+dubbo.registry.address=10.20.153.28:9090
+dubbo.provider.port=20880
+dubbo.provider.protocol=dubbo
+dubbo.provider.codec=
+dubbo.provider.client=
+dubbo.provider.server=
+dubbo.provider.serialization=
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/test/resources/log4j.properties b/dubbo-test/dubbo-test-benchmark/src/test/resources/log4j.properties
new file mode 100644
index 0000000..02bc05e
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/test/resources/log4j.properties
@@ -0,0 +1,6 @@
+log4j.rootLogger=WARN,stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} [%t] %5p %c{1}\:%L - %m%n
+log4j.logger.com.alibaba.dubbo=WARN
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-benchmark/src/test/resources/server/dubborpcserver.sh b/dubbo-test/dubbo-test-benchmark/src/test/resources/server/dubborpcserver.sh
new file mode 100644
index 0000000..1cc54b7
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/test/resources/server/dubborpcserver.sh
@@ -0,0 +1 @@
+sh servercommon.sh "../lib" "com.alibaba.dubbo.rpc.benchmark.RpcBenchmarkServer" "rpcserver.log.dubbo"
diff --git a/dubbo-test/dubbo-test-benchmark/src/test/resources/server/dubboserver.sh b/dubbo-test/dubbo-test-benchmark/src/test/resources/server/dubboserver.sh
new file mode 100644
index 0000000..33e6522
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/test/resources/server/dubboserver.sh
@@ -0,0 +1 @@
+sh servercommon.sh "../lib" "com.alibaba.dubbo.rpc.benchmark.BenchmarkServer" "server.log.dubbo"
diff --git a/dubbo-test/dubbo-test-benchmark/src/test/resources/server/servercommon.sh b/dubbo-test/dubbo-test-benchmark/src/test/resources/server/servercommon.sh
new file mode 100644
index 0000000..68e4ef8
--- /dev/null
+++ b/dubbo-test/dubbo-test-benchmark/src/test/resources/server/servercommon.sh
@@ -0,0 +1 @@
+java -Xms2g -Xmx2g -Xmn500m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -Djava.ext.dirs=$1 $2 [listenport] [maxthreads] [responsesize] [transporter] [serialization]> $3 2>&1 &
diff --git a/dubbo-test/dubbo-test-compatibility/pom.xml b/dubbo-test/dubbo-test-compatibility/pom.xml
new file mode 100644
index 0000000..a5c8d33
--- /dev/null
+++ b/dubbo-test/dubbo-test-compatibility/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-test</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-test-compatibility</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The technology compatibility kit(TCK) test module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-test/dubbo-test-integration/pom.xml b/dubbo-test/dubbo-test-integration/pom.xml
new file mode 100644
index 0000000..3a12fd1
--- /dev/null
+++ b/dubbo-test/dubbo-test-integration/pom.xml
@@ -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.
+-->
+<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-test</artifactId>
+ <version>2.2.2</version>
+ </parent>
+ <artifactId>dubbo-test-integration</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The showcase test module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-config-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-config-spring</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-mina</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-grizzly</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-p2p</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-http</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-default</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-injvm</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-rmi</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-hessian</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-default</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-multicast</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-zookeeper</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-monitor-default</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${basedir}/src/main/java</directory>
+ <excludes>
+ <exclude>**/*.java</exclude>
+ </excludes>
+ </resource>
+ <resource>
+ <directory>${basedir}/src/main/resources</directory>
+ </resource>
+ </resources>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/dubbo-test/pom.xml b/dubbo-test/pom.xml
new file mode 100644
index 0000000..f1ee20f
--- /dev/null
+++ b/dubbo-test/pom.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.
+-->
+<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.2.2</version>
+ </parent>
+ <artifactId>dubbo-test</artifactId>
+ <version>2.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>The test module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>true</skip_maven_deploy>
+ </properties>
+ <modules>
+ <module>dubbo-test-benchmark</module>
+ <module>dubbo-test-compatibility</module>
+ <module>dubbo-test-integration</module>
+ </modules>
+</project>
diff --git a/dubbo/pom.xml b/dubbo/pom.xml
new file mode 100644
index 0000000..ae323d4
--- /dev/null
+++ b/dubbo/pom.xml
@@ -0,0 +1,383 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT 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.2.2</version>
+ </parent>
+ <artifactId>dubbo</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</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-api</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-config-spring</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-mina</artifactId>
+ <version>${project.parent.version}</version>
+ <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>
+ <exclusion>
+ <groupId>com.github.sgroschupf</groupId>
+ <artifactId>zkclient</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-redis</artifactId>
+ <version>${project.parent.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>redis.clients</groupId>
+ <artifactId>jedis</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-monitor-default</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-container-spring</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-container-jetty</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-container-log4j</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-api</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-api</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-filter-validation</include>
+ <include>com.alibaba:dubbo-filter-cache</include>
+ <include>com.alibaba:dubbo-cluster</include>
+ <include>com.alibaba:dubbo-registry-api</include>
+ <include>com.alibaba:dubbo-registry-default</include>
+ <include>com.alibaba:dubbo-registry-multicast</include>
+ <include>com.alibaba:dubbo-registry-zookeeper</include>
+ <include>com.alibaba:dubbo-registry-redis</include>
+ <include>com.alibaba:dubbo-monitor-api</include>
+ <include>com.alibaba:dubbo-monitor-default</include>
+ <include>com.alibaba:dubbo-config-api</include>
+ <include>com.alibaba:dubbo-config-spring</include>
+ <include>com.alibaba:dubbo-container-api</include>
+ <include>com.alibaba:dubbo-container-spring</include>
+ <include>com.alibaba:dubbo-container-jetty</include>
+ <include>com.alibaba:dubbo-container-log4j</include>
+ </includes>
+ </artifactSet>
+ <transformers>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.container.Container</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.remoting.Dispather</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.remoting.Codec</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.remoting.http.HttpBinder</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.rpc.Filter</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory</resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/dubbo/com.alibaba.dubbo.validation.Validation</resource>
+ </transformer>
+ </transformers>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..836c50a
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,493 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT 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.2.2</version>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</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>
+ <modules>
+ <module>dubbo-common</module>
+ <module>dubbo-container</module>
+ <module>dubbo-remoting</module>
+ <module>dubbo-rpc</module>
+ <module>dubbo-filter</module>
+ <module>dubbo-cluster</module>
+ <module>dubbo-registry</module>
+ <module>dubbo-monitor</module>
+ <module>dubbo-config</module>
+ <module>dubbo</module>
+ <module>dubbo-simple</module>
+ <module>dubbo-demo</module>
+ <module>dubbo-test</module>
+ </modules>
+ <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-fixed-2</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>
+ <zkclient_version>0.1</zkclient_version>
+ <jedis_version>2.0.0</jedis_version>
+ <xmemcached_version>1.3.6</xmemcached_version>
+ <cxf_version>2.6.0</cxf_version>
+ <thrift_version>0.8.0</thrift_version>
+ <jfreechart_version>1.0.13</jfreechart_version>
+ <hessian_version>4.0.7</hessian_version>
+ <servlet_version>2.5</servlet_version>
+ <jetty_version>6.1.26</jetty_version>
+ <validation_version>1.0.0.GA</validation_version>
+ <hibernate_validator_version>4.2.0.Final</hibernate_validator_version>
+ <jcache_version>0.4</jcache_version>
+ <sca_version>2.0-M5.1</sca_version>
+ <guice_version>3.0</guice_version>
+ <!-- Log libs -->
+ <slf4j_version>1.6.2</slf4j_version>
+ <jcl_version>1.1</jcl_version>
+ <log4j_version>1.2.16</log4j_version>
+ <!-- Test libs -->
+ <junit_version>4.10</junit_version>
+ <easymock_version>3.0</easymock_version>
+ <jmockit_version>0.999.8</jmockit_version>
+ <!-- Build args -->
+ <!--<argline>-server -Xms64m -Xmx64m -XX:PermSize=64m -XX:MaxPermSize=64m</argline>-->
+ <skip_maven_deploy>false</skip_maven_deploy>
+ <updateReleaseInfo>true</updateReleaseInfo>
+ <project.build.sourceEncoding>${file_encoding}</project.build.sourceEncoding>
+ </properties>
+ <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>com.github.sgroschupf</groupId>
+ <artifactId>zkclient</artifactId>
+ <version>${zkclient_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>redis.clients</groupId>
+ <artifactId>jedis</artifactId>
+ <version>${jedis_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.googlecode.xmemcached</groupId>
+ <artifactId>xmemcached</artifactId>
+ <version>${xmemcached_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxws</artifactId>
+ <version>${cxf_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.thrift</groupId>
+ <artifactId>libthrift</artifactId>
+ <version>${thrift_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>jfree</groupId>
+ <artifactId>jfreechart</artifactId>
+ <version>${jfreechart_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.caucho</groupId>
+ <artifactId>hessian</artifactId>
+ <version>${hessian_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>${servlet_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty</artifactId>
+ <version>${jetty_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <version>${validation_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ <version>${hibernate_validator_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.cache</groupId>
+ <artifactId>cache-api</artifactId>
+ <version>${jcache_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-sca-api</artifactId>
+ <version>${sca_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ <version>${guice_version}</version>
+ </dependency>
+ <!-- Log libs -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging-api</artifactId>
+ <version>${jcl_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>${log4j_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>
+ <forkMode>once</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>
+ <profiles>
+ <profile>
+ <id>hudson</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <testFailureIgnore>true</testFailureIgnore>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+ <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>LvGang(Kimi)</name>
+ <id>gang.lvg</id>
+ <email>gang.lvg (AT) alibaba-inc.com</email>
+ <roles>
+ <role>Developer</role>
+ </roles>
+ <timezone>+8</timezone>
+ </developer>
+ </developers>
+</project>